#include "TableView.h" namespace ezui { TableView::TableView(Object* parentObject) : Control(parentObject) { Init(); } TableView::~TableView() { EndEdit(false); // 注意:m_editBox、m_editCombo、m_editCheck 作为子控件,由父控件自动管理 // 不需要手动 delete,否则会导致双重释放 m_editBox = nullptr; m_editCombo = nullptr; m_editCheck = nullptr; } void TableView::Init() { // 初始化垂直滚动条 m_vScrollBar.SetWidth(10); m_vScrollBar.Parent = this; m_vScrollBar.OffsetCallback = [this](int offset) { this->OffsetY(offset); }; // 初始化水平滚动条 m_hScrollBar.SetHeight(10); m_hScrollBar.Parent = this; m_hScrollBar.OffsetCallback = [this](int offset) { this->OffsetX(offset); }; // 创建编辑控件(初始隐藏) m_editBox = new TextBox(); m_editBox->SetVisible(false); // 设置为:不自动换行,但允许手动换行(Shift+Enter) m_editBox->SetMultiLine(false, true); // 为编辑框设置默认字体样式 m_editBox->Style.FontSize = m_cellFontSize; m_editBox->Style.FontFamily = L"Microsoft YaHei"; // 添加到子控件列表,这样才能正确处理事件和绘制 this->Add(m_editBox); m_editBox->TextChanged = [this](const UIString& text) { if (m_editing && m_editRow >= 0 && m_editCol >= 0) { // 实时更新数据 if (m_editRow < (int)m_data.size() && m_editCol < (int)m_data[m_editRow].size()) { m_data[m_editRow][m_editCol].Text = text; UpdateRowHeight(m_editRow); // 同步更新编辑框的高度以匹配新的行高 if (m_editCol < (int)m_columns.size() && m_editRow < (int)m_rowHeights.size()) { int x = GetColumnX(m_editCol); int y = GetRowY(m_editRow); int width = m_columns[m_editCol].Width; int height = m_rowHeights[m_editRow]; // 使用更新后的行高 m_editBox->SetRect(Rect(x, y, width, height)); } if (CellValueChanged) { CellValueChanged(m_editRow, m_editCol, text); } } } }; } int TableView::GetContentWidth() const { int width = m_firstColumnWidth; for (const auto& col : m_columns) { width += col.Width; } return width; } int TableView::GetContentHeight() const { int height = m_headerHeight; for (int h : m_rowHeights) { height += h; } return height; } int TableView::GetColumnX(int colIndex) const { int x = m_firstColumnWidth - m_scrollOffsetX; for (int i = 0; i < colIndex && i < (int)m_columns.size(); ++i) { x += m_columns[i].Width; } return x; } int TableView::GetRowY(int rowIndex) const { int y = m_headerHeight - m_scrollOffsetY; for (int i = 0; i < rowIndex && i < (int)m_rowHeights.size(); ++i) { y += m_rowHeights[i]; } return y; } bool TableView::HitTestCell(const Point& pt, int* outRow, int* outCol) const { // 检查是否在表头区域 if (pt.Y < m_headerHeight) { *outRow = -1; // 检查是否在第一列 if (pt.X < m_firstColumnWidth) { *outCol = -1; return true; } // 检查是否在数据列 int x = m_firstColumnWidth; for (int i = 0; i < (int)m_columns.size(); ++i) { if (pt.X >= x - m_scrollOffsetX && pt.X < x - m_scrollOffsetX + m_columns[i].Width) { *outCol = i; return true; } x += m_columns[i].Width; } return false; } // 检查行 int y = m_headerHeight; for (int row = 0; row < (int)m_rowHeights.size(); ++row) { int rowY = y - m_scrollOffsetY; if (pt.Y >= rowY && pt.Y < rowY + m_rowHeights[row]) { *outRow = row; // 检查列 if (pt.X < m_firstColumnWidth) { *outCol = -1; // 第一列 return true; } int x = m_firstColumnWidth; for (int col = 0; col < (int)m_columns.size(); ++col) { int colX = x - m_scrollOffsetX; if (pt.X >= colX && pt.X < colX + m_columns[col].Width) { *outCol = col; return true; } x += m_columns[col].Width; } return false; } y += m_rowHeights[row]; } *outRow = -1; *outCol = -1; return false; } int TableView::HitTestColumnBorder(const Point& pt) const { const int borderHitWidth = 5; // 边界检测宽度 // 只在表头区域检测 if (pt.Y >= m_headerHeight) { return -2; // 返回-2表示不在表头区域,不应该显示拖动光标 } // 检测第一列右边界 int x = m_firstColumnWidth; if (std::abs(pt.X - x) <= borderHitWidth) { return -1; // 返回-1表示第一列边界(特殊处理) } // 检测数据列边界 for (int i = 0; i < (int)m_columns.size(); ++i) { x += m_columns[i].Width; int borderX = x - m_scrollOffsetX; if (std::abs(pt.X - borderX) <= borderHitWidth) { return i; } } return -2; // 未命中任何边界 } bool TableView::HitTestVScrollBar(const Point& pt) { if (!m_vScrollBar.IsVisible()) { return false; } Rect scrollRect = m_vScrollBar.GetRect(); return scrollRect.Contains(pt); } bool TableView::HitTestHScrollBar(const Point& pt) { if (!m_hScrollBar.IsVisible()) { return false; } Rect scrollRect = m_hScrollBar.GetRect(); return scrollRect.Contains(pt); } void TableView::DrawHeader(PaintEventArgs& args) { auto& g = args.Graphics; // 绘制表头背景 Rect headerRect(0, 0, Width(), m_headerHeight); g.SetColor(m_headerBackColor); g.FillRectangle(RectF(headerRect)); // 绘制第一列表头 Rect firstColRect(0, 0, m_firstColumnWidth, m_headerHeight); g.SetColor(m_headerBackColor); g.FillRectangle(RectF(firstColRect)); // 如果第一列是 CheckBox 类型,绘制全选复选框 if (m_firstColumnType == FirstColumnType::CheckBox) { int checkSize = (std::min)(m_headerHeight - 8, 16); int checkX = (m_firstColumnWidth - checkSize) / 2; int checkY = (m_headerHeight - checkSize) / 2; Rect checkRect(checkX, checkY, checkSize, checkSize); // 绘制复选框边框 g.SetColor(m_cellBorderColor); g.DrawRectangle(RectF(checkRect)); // 如果全选,绘制勾选标记 if (m_headerSelectAll) { g.SetColor(m_headerForeColor); // 简单的勾选线 PointF p1(checkX + 3, checkY + checkSize / 2); PointF p2(checkX + checkSize / 3, checkY + checkSize - 4); PointF p3(checkX + checkSize - 3, checkY + 4); g.DrawLine(p1, p2); g.DrawLine(p2, p3); } } else if (m_firstColumnType == FirstColumnType::Index) { // 绘制 "#" 或序号标题 Font font(m_cellFontFamily, m_cellFontSize); g.SetFont(font); g.SetColor(m_headerForeColor); TextLayout layout(L"#", font, SizeF(m_firstColumnWidth, m_headerHeight), TextAlign::MiddleCenter); g.DrawTextLayout(layout, PointF(0, 0)); } // 绘制第一列边框 if (m_cellBorderStyle != StrokeStyle::None) { g.SetColor(m_cellBorderColor); g.DrawRectangle(RectF(firstColRect)); } // 绘制数据列表头 int x = m_firstColumnWidth - m_scrollOffsetX; for (int i = 0; i < (int)m_columns.size(); ++i) { const auto& col = m_columns[i]; // 裁剪到可见区域 if (x + col.Width <= m_firstColumnWidth || x >= Width()) { x += col.Width; continue; } Rect colRect(x, 0, col.Width, m_headerHeight); // 绘制表头文字 Font font(m_cellFontFamily, m_cellFontSize); g.SetFont(font); g.SetColor(m_headerForeColor); // 添加排序指示器 std::wstring headerText = col.HeaderText.unicode(); if (col.CurrentSort == SortOrder::Ascending) { headerText = headerText + L" ▲"; } else if (col.CurrentSort == SortOrder::Descending) { headerText = headerText + L" ▼"; } TextLayout layout(headerText, font, SizeF(col.Width - 4, m_headerHeight), TextAlign::MiddleCenter); g.DrawTextLayout(layout, PointF(x + 2, 0)); // 绘制边框 if (m_cellBorderStyle != StrokeStyle::None) { g.SetColor(m_cellBorderColor); g.DrawRectangle(RectF(colRect)); } x += col.Width; } } void TableView::DrawCells(PaintEventArgs& args) { int startY = m_headerHeight; for (int row = 0; row < (int)m_data.size(); ++row) { int rowY = startY + GetRowY(row) - m_headerHeight; int rowHeight = m_rowHeights[row]; // 跳过不可见行 if (rowY + rowHeight <= m_headerHeight || rowY >= Height()) { continue; } // 绘制第一列 Rect firstColRect(0, rowY, m_firstColumnWidth, rowHeight); DrawFirstColumn(args, row, firstColRect); // 绘制数据列 int x = m_firstColumnWidth - m_scrollOffsetX; for (int col = 0; col < (int)m_columns.size(); ++col) { int colWidth = m_columns[col].Width; // 跳过不可见列 if (x + colWidth <= m_firstColumnWidth || x >= Width()) { x += colWidth; continue; } Rect cellRect(x, rowY, colWidth, rowHeight); DrawCell(args, row, col, cellRect); x += colWidth; } } } void TableView::DrawFirstColumn(PaintEventArgs& args, int row, const Rect& cellRect) { auto& g = args.Graphics; // 获取行选中状态背景色 Color backColor = m_cellBackColor; if (m_firstColumnType == FirstColumnType::CheckBox && row < (int)m_rowChecked.size() && m_rowChecked[row]) { backColor = SelectedRowBackColor; } // 如果第一列不是 CheckBox 且当前行被选中,使用选中行背景色 if (m_firstColumnType != FirstColumnType::CheckBox && row == m_selectedRow) { backColor = SelectedRowBackColor; } // 绘制背景 g.SetColor(backColor); g.FillRectangle(RectF(cellRect)); // 根据第一列类型绘制内容 if (m_firstColumnType == FirstColumnType::Index) { // 绘制序号 Font font(m_cellFontFamily, m_cellFontSize); g.SetFont(font); g.SetColor(m_cellForeColor); std::wstring indexText = std::to_wstring(row + 1); // 从1开始 TextLayout layout(indexText, font, SizeF(m_firstColumnWidth - 4, cellRect.Height), TextAlign::MiddleCenter); g.DrawTextLayout(layout, PointF(cellRect.X + 2, cellRect.Y)); } else if (m_firstColumnType == FirstColumnType::CheckBox) { // 绘制复选框 int checkSize = (std::min)(cellRect.Height - 8, 16); int checkX = cellRect.X + (m_firstColumnWidth - checkSize) / 2; int checkY = cellRect.Y + (cellRect.Height - checkSize) / 2; Rect checkRect(checkX, checkY, checkSize, checkSize); g.SetColor(m_cellBorderColor); g.DrawRectangle(RectF(checkRect)); if (row < (int)m_rowChecked.size() && m_rowChecked[row]) { g.SetColor(m_cellForeColor); PointF p1(checkX + 3, checkY + checkSize / 2); PointF p2(checkX + checkSize / 3, checkY + checkSize - 4); PointF p3(checkX + checkSize - 3, checkY + 4); g.DrawLine(p1, p2); g.DrawLine(p2, p3); } } // TextBox 类型第一列留空 // 绘制边框 if (m_cellBorderStyle != StrokeStyle::None) { g.SetColor(m_cellBorderColor); g.DrawRectangle(RectF(cellRect)); } } void TableView::DrawCell(PaintEventArgs& args, int row, int col, const Rect& cellRect) { auto& g = args.Graphics; if (row >= (int)m_data.size() || col >= (int)m_data[row].size()) { return; } const auto& cellData = m_data[row][col]; const auto& colInfo = m_columns[col]; // 确定背景色 Color backColor = m_cellBackColor; // 如果第一列是 CheckBox 且选中,使用选中行背景色 if (m_firstColumnType == FirstColumnType::CheckBox && row < (int)m_rowChecked.size() && m_rowChecked[row]) { backColor = SelectedRowBackColor; } // 如果第一列不是 CheckBox 且当前行被选中,使用选中行背景色 if (m_firstColumnType != FirstColumnType::CheckBox && row == m_selectedRow) { backColor = SelectedRowBackColor; } // 如果单元格有独立背景色,使用独立设置(优先级高) if (cellData.Style.HasBackColor) { backColor = cellData.Style.BackColor; } // 如果正在编辑此单元格,跳过内容绘制(只绘制背景),让 TextBox 绘制内容 bool isEditing = m_editing && m_editRow == row && m_editCol == col; // 绘制背景(编辑时也需要绘制,但使用白色以匹配 TextBox) if (isEditing) { g.SetColor(Color::White); } else { g.SetColor(backColor); } g.FillRectangle(RectF(cellRect)); // 如果正在编辑此单元格,跳过内容绘制 if (isEditing) { return; } // 确定前景色 Color foreColor = m_cellForeColor; if (cellData.Style.HasForeColor) { foreColor = cellData.Style.ForeColor; } // 根据单元格类型绘制内容 Font font(m_cellFontFamily, m_cellFontSize); g.SetFont(font); g.SetColor(foreColor); // 获取对齐方式 TextAlign textAlign = GetCellTextAlign(row, col); switch (colInfo.Type) { case CellType::TextBox: case CellType::ReadOnly: { // 绘制文本(禁用自动换行,只根据实际换行符换行) font.Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); // 使用单元格实际高度,这样Middle和Bottom对齐才能正确显示 TextLayout layout(cellData.Text.unicode(), font, SizeF(cellRect.Width - 4, cellRect.Height - 2), textAlign); g.DrawTextLayout(layout, PointF(cellRect.X + 2, cellRect.Y + 1)); break; } case CellType::CheckBox: { // 绘制复选框 int checkSize = (std::min)(cellRect.Height - 8, 16); int checkX = cellRect.X + (cellRect.Width - checkSize) / 2; int checkY = cellRect.Y + (cellRect.Height - checkSize) / 2; Rect checkRect(checkX, checkY, checkSize, checkSize); g.SetColor(m_cellBorderColor); g.DrawRectangle(RectF(checkRect)); if (cellData.Checked) { g.SetColor(foreColor); PointF p1(checkX + 3, checkY + checkSize / 2); PointF p2(checkX + checkSize / 3, checkY + checkSize - 4); PointF p3(checkX + checkSize - 3, checkY + 4); g.DrawLine(p1, p2); g.DrawLine(p2, p3); } break; } case CellType::ComboBox: { // 绘制下拉框文本 UIString displayText; if (cellData.ComboIndex >= 0 && cellData.ComboIndex < (int)colInfo.ComboItems.size()) { displayText = colInfo.ComboItems[cellData.ComboIndex]; } TextLayout layout(displayText.unicode(), font, SizeF(cellRect.Width - 20, cellRect.Height - 2), TextAlign::MiddleLeft); g.DrawTextLayout(layout, PointF(cellRect.X + 2, cellRect.Y + 1)); // 绘制下拉箭头 int arrowSize = 8; int arrowX = cellRect.GetRight() - arrowSize - 4; int arrowY = cellRect.Y + (cellRect.Height - arrowSize / 2) / 2; PointF ap1(arrowX, arrowY); PointF ap2(arrowX + arrowSize, arrowY); PointF ap3(arrowX + arrowSize / 2, arrowY + arrowSize / 2); g.SetColor(foreColor); g.DrawLine(ap1, ap3); g.DrawLine(ap2, ap3); break; } } // 绘制边框 StrokeStyle borderStyle = m_cellBorderStyle; Color borderColor = m_cellBorderColor; if (cellData.Style.HasBorderStyle) { borderStyle = cellData.Style.BorderStyle; } if (cellData.Style.HasBorderColor) { borderColor = cellData.Style.BorderColor; } if (borderStyle != StrokeStyle::None) { g.SetColor(borderColor); // TODO: 支持虚线样式 g.DrawRectangle(RectF(cellRect)); } } void TableView::BeginEdit(int row, int col) { if (row < 0 || col < 0 || row >= (int)m_data.size() || col >= (int)m_columns.size()) { return; } const auto& colInfo = m_columns[col]; // ReadOnly 类型不能编辑 if (colInfo.Type == CellType::ReadOnly) { return; } EndEdit(true); // 结束之前的编辑 // 保存编辑前的原始值 if (colInfo.Type == CellType::TextBox) { m_editOriginalValue = m_data[row][col].Text; } else if (colInfo.Type == CellType::CheckBox) { m_editOriginalValue = m_data[row][col].Checked ? L"true" : L"false"; } else if (colInfo.Type == CellType::ComboBox) { int idx = m_data[row][col].ComboIndex; if (idx >= 0 && idx < (int)colInfo.ComboItems.size()) { m_editOriginalValue = colInfo.ComboItems[idx]; } else { m_editOriginalValue = L""; } } m_editing = true; m_editRow = row; m_editCol = col; // 计算单元格位置 int x = GetColumnX(col); int y = GetRowY(row); int width = colInfo.Width; int height = m_rowHeights[row]; Rect editRect(x, y, width, height); switch (colInfo.Type) { case CellType::TextBox: { // 确保 TextBox 字体与单元格字体一致 m_editBox->Style.FontSize = m_cellFontSize; m_editBox->Style.FontFamily = m_cellFontFamily; m_editBox->SetRect(editRect); m_editBox->SetText(m_data[row][col].Text); m_editBox->SetVisible(true); // 确保 TextBox 获得正确的窗口句柄(从 TableView 继承) m_editBox->SetHwnd(this->Hwnd()); // 触发焦点事件(这会启动光标闪烁计时器) m_editBox->SendEvent(FocusEventArgs(m_editBox)); Invalidate(); break; } case CellType::CheckBox: { // 直接切换状态 m_data[row][col].Checked = !m_data[row][col].Checked; m_editing = false; m_editRow = -1; m_editCol = -1; if (CellValueChanged) { CellValueChanged(row, col, m_data[row][col].Checked ? L"true" : L"false"); } Invalidate(); break; } case CellType::ComboBox: { // TODO: 显示下拉列表 // 暂时简化处理,循环选择 int nextIndex = (m_data[row][col].ComboIndex + 1) % (std::max)(1, (int)colInfo.ComboItems.size()); m_data[row][col].ComboIndex = nextIndex; m_editing = false; m_editRow = -1; m_editCol = -1; UIString newValue; if (nextIndex >= 0 && nextIndex < (int)colInfo.ComboItems.size()) { newValue = colInfo.ComboItems[nextIndex]; } if (CellValueChanged) { CellValueChanged(row, col, newValue); } Invalidate(); break; } default: m_editing = false; m_editRow = -1; m_editCol = -1; break; } } void TableView::EndEdit(bool save) { if (!m_editing) { return; } int editRow = m_editRow; int editCol = m_editCol; UIString newValue; if (save && editRow >= 0 && editCol >= 0) { if (editRow < (int)m_data.size() && editCol < (int)m_data[editRow].size()) { const auto& colInfo = m_columns[editCol]; if (colInfo.Type == CellType::TextBox) { newValue = m_editBox->GetText(); m_data[editRow][editCol].Text = newValue; UpdateRowHeight(editRow); // 触发编辑完成回调 if (CellEditFinished && newValue != m_editOriginalValue) { CellEditFinished(editRow, editCol, m_editOriginalValue, newValue); } } } } m_editBox->SetVisible(false); m_editing = false; m_editRow = -1; m_editCol = -1; Invalidate(); } void TableView::UpdateRowHeight(int row) { if (row < 0 || row >= (int)m_data.size()) { return; } int maxHeight = m_defaultRowHeight; // 遍历该行所有单元格,计算最大需要的高度 for (int col = 0; col < (int)m_data[row].size() && col < (int)m_columns.size(); ++col) { const auto& cellData = m_data[row][col]; const auto& colInfo = m_columns[col]; if (colInfo.Type == CellType::TextBox || colInfo.Type == CellType::ReadOnly) { int lines = CalculateTextLines(cellData.Text, colInfo.Width - 4); int neededHeight = lines * (m_cellFontSize + 4) + 8; if (neededHeight > maxHeight) { maxHeight = neededHeight; } } } if (row < (int)m_rowHeights.size()) { m_rowHeights[row] = maxHeight; } RefreshScrollBars(); Invalidate(); } int TableView::CalculateTextLines(const UIString& text, int width) const { if (text.empty() || width <= 0) { return 1; } // 由于单元格设置为不自动换行,只根据实际换行符计算行数 std::wstring wtext = text.unicode(); int lines = 1; for (wchar_t c : wtext) { if (c == L'\n') { lines++; } } return lines; } void TableView::RefreshScrollBars() { int contentWidth = GetContentWidth(); int contentHeight = GetContentHeight(); // 可视区域大小(初步计算,不考虑滚动条) int viewWidth = Width(); int viewHeight = Height() - m_headerHeight; // 判断是否需要滚动条 bool needVScroll = (contentHeight - m_headerHeight) > viewHeight; bool needHScroll = contentWidth > viewWidth; // 如果需要垂直滚动条,水平可视区域要减去滚动条宽度 if (needVScroll) { viewWidth -= m_vScrollBar.Width(); // 重新判断是否需要水平滚动条 needHScroll = contentWidth > viewWidth; } // 如果需要水平滚动条,垂直可视区域要减去滚动条高度 if (needHScroll) { viewHeight -= m_hScrollBar.Height(); // 重新判断是否需要垂直滚动条 if (!needVScroll) { needVScroll = (contentHeight - m_headerHeight) > viewHeight; if (needVScroll) { viewWidth -= m_vScrollBar.Width(); } } } // 设置垂直滚动条 if (needVScroll) { m_vScrollBar.SetVisible(true); int vScrollHeight = Height() - m_headerHeight - (needHScroll ? m_hScrollBar.Height() : 0); m_vScrollBar.SetRect(Rect(Width() - m_vScrollBar.Width(), m_headerHeight, m_vScrollBar.Width(), vScrollHeight)); } else { m_vScrollBar.SetVisible(false); m_scrollOffsetY = 0; } // 设置水平滚动条 if (needHScroll) { m_hScrollBar.SetVisible(true); int hScrollWidth = Width() - m_firstColumnWidth - (needVScroll ? m_vScrollBar.Width() : 0); m_hScrollBar.SetRect(Rect(m_firstColumnWidth, Height() - m_hScrollBar.Height(), hScrollWidth, m_hScrollBar.Height())); } else { m_hScrollBar.SetVisible(false); m_scrollOffsetX = 0; } m_vScrollBar.RefreshScroll(); m_hScrollBar.RefreshScroll(); } void TableView::OffsetX(int offset) { m_scrollOffsetX = -offset; Invalidate(); } void TableView::OffsetY(int offset) { m_scrollOffsetY = -offset; Invalidate(); } void TableView::DoSort(int colIndex) { if (colIndex < 0 || colIndex >= (int)m_columns.size()) { return; } // 更新排序状态 SortOrder newOrder = SortOrder::Ascending; if (m_columns[colIndex].CurrentSort == SortOrder::Ascending) { newOrder = SortOrder::Descending; } else if (m_columns[colIndex].CurrentSort == SortOrder::Descending) { newOrder = SortOrder::None; } // 重置其他列的排序状态 for (auto& col : m_columns) { col.CurrentSort = SortOrder::None; } m_columns[colIndex].CurrentSort = newOrder; if (newOrder == SortOrder::None) { Invalidate(); return; } // 创建索引数组进行排序 std::vector indices(m_data.size()); for (int i = 0; i < (int)indices.size(); ++i) { indices[i] = i; } // 排序 std::sort(indices.begin(), indices.end(), [this, colIndex, newOrder](int a, int b) { UIString textA, textB; if (colIndex < (int)m_data[a].size()) { textA = m_data[a][colIndex].Text; } if (colIndex < (int)m_data[b].size()) { textB = m_data[b][colIndex].Text; } if (newOrder == SortOrder::Ascending) { return textA < textB; } else { return textA > textB; } }); // 重新排列数据 std::vector> newData(m_data.size()); std::vector newRowHeights(m_rowHeights.size()); std::vector newRowChecked(m_rowChecked.size()); std::vector newRowTextAlign(m_rowTextAlign.size()); for (int i = 0; i < (int)indices.size(); ++i) { int oldIndex = indices[i]; newData[i] = std::move(m_data[oldIndex]); if (oldIndex < (int)m_rowHeights.size()) { newRowHeights[i] = m_rowHeights[oldIndex]; } if (oldIndex < (int)m_rowChecked.size()) { newRowChecked[i] = m_rowChecked[oldIndex]; } if (oldIndex < (int)m_rowTextAlign.size()) { newRowTextAlign[i] = m_rowTextAlign[oldIndex]; } } m_data = std::move(newData); m_rowHeights = std::move(newRowHeights); m_rowChecked = std::move(newRowChecked); m_rowTextAlign = std::move(newRowTextAlign); Invalidate(); } // ============ 重写的虚函数 ============ void TableView::OnPaint(PaintEventArgs& args) { __super::OnPaint(args); // 绘制单元格(先绘制,这样表头会覆盖在上面) DrawCells(args); // 绘制表头 DrawHeader(args); // 绘制滚动条 if (m_vScrollBar.IsVisible()) { m_vScrollBar.SendEvent(args); } if (m_hScrollBar.IsVisible()) { m_hScrollBar.SendEvent(args); } // 绘制编辑控件(放在最后确保在最上层) if (m_editBox->IsVisible()) { m_editBox->SendEvent(args); } } void TableView::OnChildPaint(PaintEventArgs& args) { // 重写子控件绘制,跳过 m_editBox(因为我们在 OnPaint 中单独处理) // 其他子控件正常绘制 ViewControls.clear(); Rect rect(0, 0, Width(), Height()); for (auto& it : GetControls()) { if (it == m_editBox) { continue; // 跳过编辑框,由 OnPaint 单独处理 } if (rect.IntersectsWith(it->GetRect())) { ViewControls.push_back(it); } it->SendEvent(args); } } void TableView::OnLayout() { // 设置滚动条位置 m_vScrollBar.SetRect(Rect(Width() - m_vScrollBar.Width(), m_headerHeight, m_vScrollBar.Width(), Height() - m_headerHeight - (m_hScrollBar.IsVisible() ? m_hScrollBar.Height() : 0))); m_hScrollBar.SetRect(Rect(m_firstColumnWidth, Height() - m_hScrollBar.Height(), Width() - m_firstColumnWidth - (m_vScrollBar.IsVisible() ? m_vScrollBar.Width() : 0), m_hScrollBar.Height())); RefreshScrollBars(); __super::OnLayout(); } void TableView::OnMouseMove(const MouseEventArgs& args) { __super::OnMouseMove(args); // 转发事件给编辑框(用于文本选择) if (m_editBox->IsVisible()) { Rect editRect = m_editBox->GetRect(); m_editBox->SendEvent(MouseEventArgs(Event::OnMouseMove, Point(args.Location.X - editRect.X, args.Location.Y - editRect.Y), args.Button, args.ZDelta)); } // 转发事件给滚动条 if (m_vScrollBar.IsVisible()) { m_vScrollBar.SendEvent(MouseEventArgs(Event::OnMouseMove, Point(args.Location.X - m_vScrollBar.X(), args.Location.Y - m_vScrollBar.Y()), args.Button, args.ZDelta)); } if (m_hScrollBar.IsVisible()) { m_hScrollBar.SendEvent(MouseEventArgs(Event::OnMouseMove, Point(args.Location.X - m_hScrollBar.X(), args.Location.Y - m_hScrollBar.Y()), args.Button, args.ZDelta)); } // 列宽拖动 if (m_draggingColumn) { int delta = args.Location.X - m_dragStartX; int newWidth = (std::max)(20, m_dragStartWidth + delta); if (m_dragColumnIndex == -1) { // 拖动第一列 m_firstColumnWidth = newWidth; } else if (m_dragColumnIndex >= 0 && m_dragColumnIndex < (int)m_columns.size()) { m_columns[m_dragColumnIndex].Width = newWidth; } RefreshScrollBars(); Invalidate(); return; } // 检测鼠标是否在列边界上 int borderCol = HitTestColumnBorder(args.Location); if (borderCol >= -1) { // -1 表示第一列边界 // 设置调整大小的光标 Style.Cursor = LoadCursor(Cursor::SIZEWE); } else { Style.Cursor = LoadCursor(Cursor::ARROW); } // 更新悬停状态 int row, col; if (HitTestCell(args.Location, &row, &col)) { if (m_hoverRow != row || m_hoverCol != col) { m_hoverRow = row; m_hoverCol = col; Invalidate(); } } } void TableView::OnMouseDown(const MouseEventArgs& args) { __super::OnMouseDown(args); // 右键单击 if (args.Button == MouseButton::Right) { int row, col; if (HitTestCell(args.Location, &row, &col)) { if (RightClick) { RightClick(row, col); } } return; } if (args.Button != MouseButton::Left) { return; } // 检查是否点击在编辑框上 if (m_editBox->IsVisible()) { Rect editRect = m_editBox->GetRect(); if (editRect.Contains(args.Location)) { m_editBox->SendEvent(MouseEventArgs(Event::OnMouseDown, Point(args.Location.X - editRect.X, args.Location.Y - editRect.Y), args.Button, args.ZDelta)); return; } } // 检查是否点击在滚动条上 if (HitTestVScrollBar(args.Location)) { m_vScrollBar.SendEvent(MouseEventArgs(Event::OnMouseDown, Point(args.Location.X - m_vScrollBar.X(), args.Location.Y - m_vScrollBar.Y()), args.Button, args.ZDelta)); return; } if (HitTestHScrollBar(args.Location)) { m_hScrollBar.SendEvent(MouseEventArgs(Event::OnMouseDown, Point(args.Location.X - m_hScrollBar.X(), args.Location.Y - m_hScrollBar.Y()), args.Button, args.ZDelta)); return; } // 检测是否点击在列边界上 int borderCol = HitTestColumnBorder(args.Location); if (borderCol >= -1) { m_draggingColumn = true; m_dragColumnIndex = borderCol; m_dragStartX = args.Location.X; if (borderCol == -1) { m_dragStartWidth = m_firstColumnWidth; } else { m_dragStartWidth = m_columns[borderCol].Width; } return; } int row, col; if (!HitTestCell(args.Location, &row, &col)) { EndEdit(true); return; } // 点击表头 if (row == -1) { if (col == -1) { // 点击第一列表头(全选) if (m_firstColumnType == FirstColumnType::CheckBox) { m_headerSelectAll = !m_headerSelectAll; for (int i = 0; i < (int)m_rowChecked.size(); ++i) { m_rowChecked[i] = m_headerSelectAll; } Invalidate(); } } else { // 点击数据列表头(排序) DoSort(col); } return; } // 点击第一列 if (col == -1) { if (m_firstColumnType == FirstColumnType::CheckBox && row < (int)m_rowChecked.size()) { // CheckBox 模式:切换该行的选中状态 m_rowChecked[row] = !m_rowChecked[row]; // 更新全选状态 bool allChecked = true; for (bool checked : m_rowChecked) { if (!checked) { allChecked = false; break; } } m_headerSelectAll = allChecked; Invalidate(); } else if (m_firstColumnType != FirstColumnType::CheckBox) { // 非 CheckBox 模式:单击选中当前行 m_selectedRow = row; Invalidate(); } return; } // 点击数据单元格 // 获取单元格类型 CellType cellType = CellType::TextBox; if (col >= 0 && col < (int)m_columns.size()) { cellType = m_columns[col].Type; } // CheckBox 和 ComboBox 单击就触发 if (cellType == CellType::CheckBox || cellType == CellType::ComboBox) { BeginEdit(row, col); m_lastClickTime = 0; m_lastClickRow = -1; m_lastClickCol = -1; return; } // TextBox 类型需要双击才进入编辑 ULONGLONG currentTime = ::GetTickCount64(); if (currentTime - m_lastClickTime < 300 && m_lastClickRow == row && m_lastClickCol == col) { // 双击,开始编辑 BeginEdit(row, col); m_lastClickTime = 0; // 重置,避免三击触发 m_lastClickRow = -1; m_lastClickCol = -1; } else { // 单击,记录时间和位置 m_lastClickTime = currentTime; m_lastClickRow = row; m_lastClickCol = col; EndEdit(true); } } void TableView::OnMouseUp(const MouseEventArgs& args) { __super::OnMouseUp(args); m_draggingColumn = false; // 转发事件给编辑框 if (m_editBox->IsVisible()) { Rect editRect = m_editBox->GetRect(); m_editBox->SendEvent(MouseEventArgs(Event::OnMouseUp, Point(args.Location.X - editRect.X, args.Location.Y - editRect.Y), args.Button, args.ZDelta)); } // 转发事件给滚动条 if (m_vScrollBar.IsVisible()) { m_vScrollBar.SendEvent(MouseEventArgs(Event::OnMouseUp, Point(args.Location.X - m_vScrollBar.X(), args.Location.Y - m_vScrollBar.Y()), args.Button, args.ZDelta)); } if (m_hScrollBar.IsVisible()) { m_hScrollBar.SendEvent(MouseEventArgs(Event::OnMouseUp, Point(args.Location.X - m_hScrollBar.X(), args.Location.Y - m_hScrollBar.Y()), args.Button, args.ZDelta)); } } void TableView::OnMouseDoubleClick(const MouseEventArgs& args) { __super::OnMouseDoubleClick(args); int row, col; if (HitTestCell(args.Location, &row, &col) && row >= 0 && col >= 0) { BeginEdit(row, col); } } void TableView::OnMouseWheel(const MouseEventArgs& args) { __super::OnMouseWheel(args); if (m_vScrollBar.IsVisible()) { m_vScrollBar.SendEvent(MouseEventArgs(Event::OnMouseWheel, args.Location, args.Button, args.ZDelta)); } } void TableView::OnMouseLeave(const MouseEventArgs& args) { __super::OnMouseLeave(args); m_hoverRow = -1; m_hoverCol = -1; Style.Cursor = LoadCursor(Cursor::ARROW); } void TableView::OnSize(const SizeEventArgs& args) { __super::OnSize(args); RefreshScrollBars(); } void TableView::OnKeyDown(const KeyboardEventArgs& args) { __super::OnKeyDown(args); // 如果编辑框可见,将键盘事件转发给编辑框 if (m_editBox->IsVisible()) { m_editBox->SendEvent(args); } if (args.wParam == VK_ESCAPE) { EndEdit(false); } else if (args.wParam == VK_RETURN && !m_editing) { // 如果不是多行编辑,回车可以结束编辑 } } void TableView::OnKeyChar(const KeyboardEventArgs& args) { __super::OnKeyChar(args); // 如果编辑框可见,将字符输入事件转发给编辑框 if (m_editBox->IsVisible()) { m_editBox->SendEvent(args); } } // ============ 公共接口实现 ============ void TableView::SetHeaders(const std::vector& headers) { m_columns.clear(); for (const auto& header : headers) { ColumnInfo col; col.HeaderText = header; col.Width = 100; m_columns.push_back(col); } // 更新现有数据的列数 for (auto& row : m_data) { row.resize(m_columns.size()); } RefreshScrollBars(); Invalidate(); } int TableView::GetColumnCount() const { return (int)m_columns.size(); } void TableView::SetHeaderHeight(int height) { m_headerHeight = (std::max)(20, height); RefreshScrollBars(); Invalidate(); } int TableView::GetHeaderHeight() const { return m_headerHeight; } void TableView::SetFirstColumnType(FirstColumnType type) { m_firstColumnType = type; // 如果切换到 CheckBox 类型,初始化选中状态 if (type == FirstColumnType::CheckBox) { m_rowChecked.resize(m_data.size(), false); } Invalidate(); } FirstColumnType TableView::GetFirstColumnType() const { return m_firstColumnType; } void TableView::SetFirstColumnWidth(int width) { m_firstColumnWidth = (std::max)(20, width); RefreshScrollBars(); Invalidate(); } int TableView::GetFirstColumnWidth() const { return m_firstColumnWidth; } void TableView::SetColumnWidth(int colIndex, int width) { if (colIndex >= 0 && colIndex < (int)m_columns.size()) { m_columns[colIndex].Width = (std::max)(20, width); RefreshScrollBars(); Invalidate(); } } int TableView::GetColumnWidth(int colIndex) const { if (colIndex >= 0 && colIndex < (int)m_columns.size()) { return m_columns[colIndex].Width; } return 0; } const std::vector TableView::GetColumnWidths() const { std::vector widths; for (const auto& col : m_columns) { widths.push_back(col.Width); } return widths; } void TableView::SetColumnType(int colIndex, CellType type) { if (colIndex >= 0 && colIndex < (int)m_columns.size()) { m_columns[colIndex].Type = type; Invalidate(); } } CellType TableView::GetColumnType(int colIndex) const { if (colIndex >= 0 && colIndex < (int)m_columns.size()) { return m_columns[colIndex].Type; } return CellType::TextBox; } void TableView::SetColumnComboItems(int colIndex, const std::vector& items) { if (colIndex >= 0 && colIndex < (int)m_columns.size()) { m_columns[colIndex].ComboItems = items; } } int TableView::GetRowCount() const { return (int)m_data.size(); } void TableView::AddRow() { std::vector newRow(m_columns.size()); m_data.push_back(newRow); m_rowHeights.push_back(m_defaultRowHeight); m_rowTextAlign.push_back(m_cellTextAlign); // 添加默认对齐 if (m_firstColumnType == FirstColumnType::CheckBox) { m_rowChecked.push_back(false); } RefreshScrollBars(); Invalidate(); } void TableView::InsertRow(int rowIndex) { if (rowIndex < 0) rowIndex = 0; if (rowIndex > (int)m_data.size()) rowIndex = (int)m_data.size(); std::vector newRow(m_columns.size()); m_data.insert(m_data.begin() + rowIndex, newRow); m_rowHeights.insert(m_rowHeights.begin() + rowIndex, m_defaultRowHeight); // 确保rowTextAlign大小与数据一致 while (m_rowTextAlign.size() < m_data.size()) { m_rowTextAlign.push_back(m_cellTextAlign); } if (rowIndex < (int)m_rowTextAlign.size()) { m_rowTextAlign.insert(m_rowTextAlign.begin() + rowIndex, m_cellTextAlign); } if (m_firstColumnType == FirstColumnType::CheckBox) { m_rowChecked.insert(m_rowChecked.begin() + rowIndex, false); } RefreshScrollBars(); Invalidate(); } void TableView::RemoveRow(int rowIndex) { if (rowIndex >= 0 && rowIndex < (int)m_data.size()) { m_data.erase(m_data.begin() + rowIndex); m_rowHeights.erase(m_rowHeights.begin() + rowIndex); if (rowIndex < (int)m_rowTextAlign.size()) { m_rowTextAlign.erase(m_rowTextAlign.begin() + rowIndex); } if (rowIndex < (int)m_rowChecked.size()) { m_rowChecked.erase(m_rowChecked.begin() + rowIndex); } RefreshScrollBars(); Invalidate(); } } void TableView::ClearRows() { m_data.clear(); m_rowHeights.clear(); m_rowChecked.clear(); m_rowTextAlign.clear(); m_headerSelectAll = false; m_scrollOffsetX = 0; m_scrollOffsetY = 0; RefreshScrollBars(); Invalidate(); } void TableView::AddColumn(const UIString& headerText, int width) { ColumnInfo col; col.HeaderText = headerText; col.Width = (std::max)(20, width); m_columns.push_back(col); // 为所有行添加新列的数据 for (auto& row : m_data) { row.push_back(CellData()); } RefreshScrollBars(); Invalidate(); } void TableView::InsertColumn(int colIndex, const UIString& headerText, int width) { if (colIndex < 0) colIndex = 0; if (colIndex > (int)m_columns.size()) colIndex = (int)m_columns.size(); ColumnInfo col; col.HeaderText = headerText; col.Width = (std::max)(20, width); m_columns.insert(m_columns.begin() + colIndex, col); // 为所有行插入新列的数据 for (auto& row : m_data) { row.insert(row.begin() + colIndex, CellData()); } RefreshScrollBars(); Invalidate(); } void TableView::RemoveColumn(int colIndex) { if (colIndex >= 0 && colIndex < (int)m_columns.size()) { m_columns.erase(m_columns.begin() + colIndex); // 从所有行删除该列的数据 for (auto& row : m_data) { if (colIndex < (int)row.size()) { row.erase(row.begin() + colIndex); } } RefreshScrollBars(); Invalidate(); } } int TableView::GetRowHeight(int rowIndex) const { if (rowIndex >= 0 && rowIndex < (int)m_rowHeights.size()) { return m_rowHeights[rowIndex]; } return m_defaultRowHeight; } const std::vector TableView::GetRowHeights() const { return m_rowHeights; } void TableView::SetDefaultRowHeight(int height) { m_defaultRowHeight = (std::max)(20, height); } void TableView::SetData(int row, int col, const UIString& value) { // 自动扩展行 while (row >= (int)m_data.size()) { AddRow(); } // 确保列足够 if (col >= 0 && col < (int)m_columns.size()) { if (col >= (int)m_data[row].size()) { m_data[row].resize(m_columns.size()); } m_data[row][col].Text = value; UpdateRowHeight(row); if (CellValueChanged) { CellValueChanged(row, col, value); } } } UIString TableView::GetData(int row, int col) const { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { return m_data[row][col].Text; } return UIString(); } void TableView::SetRowData(int row, const std::vector& values) { // 自动扩展行 while (row >= (int)m_data.size()) { AddRow(); } for (int col = 0; col < (int)values.size() && col < (int)m_columns.size(); ++col) { if (col >= (int)m_data[row].size()) { m_data[row].resize(m_columns.size()); } m_data[row][col].Text = values[col]; } UpdateRowHeight(row); } std::vector TableView::GetRowData(int row) const { std::vector result; if (row >= 0 && row < (int)m_data.size()) { for (const auto& cell : m_data[row]) { result.push_back(cell.Text); } } return result; } std::vector TableView::GetColData(int col) const { std::vector result; if (col >= 0 && col < (int)m_columns.size()) { for (const auto& row : m_data) { if (col < (int)row.size()) { result.push_back(row[col].Text); } else { result.push_back(UIString()); } } } return result; } void TableView::SetAllData(const std::vector>& data) { ClearRows(); for (const auto& rowData : data) { AddRow(); int row = (int)m_data.size() - 1; for (int col = 0; col < (int)rowData.size() && col < (int)m_columns.size(); ++col) { m_data[row][col].Text = rowData[col]; } UpdateRowHeight(row); } } std::vector> TableView::GetAllData() const { std::vector> result; for (int row = 0; row < (int)m_data.size(); ++row) { result.push_back(GetRowData(row)); } return result; } void TableView::SetCellChecked(int row, int col, bool checked) { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { m_data[row][col].Checked = checked; Invalidate(); } } bool TableView::GetCellChecked(int row, int col) const { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { return m_data[row][col].Checked; } return false; } void TableView::SetCellComboIndex(int row, int col, int index) { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { m_data[row][col].ComboIndex = index; Invalidate(); } } int TableView::GetCellComboIndex(int row, int col) const { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { return m_data[row][col].ComboIndex; } return -1; } void TableView::SetRowChecked(int row, bool checked) { if (m_firstColumnType == FirstColumnType::CheckBox && row >= 0 && row < (int)m_rowChecked.size()) { m_rowChecked[row] = checked; // 更新全选状态 bool allChecked = true; for (bool c : m_rowChecked) { if (!c) { allChecked = false; break; } } m_headerSelectAll = allChecked; Invalidate(); } } bool TableView::GetRowChecked(int row) const { if (row >= 0 && row < (int)m_rowChecked.size()) { return m_rowChecked[row]; } return false; } std::vector TableView::GetCheckedRows() const { std::vector result; for (int i = 0; i < (int)m_rowChecked.size(); ++i) { if (m_rowChecked[i]) { result.push_back(i); } } return result; } int TableView::GetSelectedRow() const { return m_selectedRow; } void TableView::SetSelectedRow(int row) { if (row >= -1 && row < (int)m_data.size()) { m_selectedRow = row; Invalidate(); } } void TableView::ClearSelection() { m_selectedRow = -1; Invalidate(); } void TableView::SelectAll() { if (m_firstColumnType == FirstColumnType::CheckBox) { m_headerSelectAll = true; for (int i = 0; i < (int)m_rowChecked.size(); ++i) { m_rowChecked[i] = true; } Invalidate(); } } void TableView::DeselectAll() { if (m_firstColumnType == FirstColumnType::CheckBox) { m_headerSelectAll = false; for (int i = 0; i < (int)m_rowChecked.size(); ++i) { m_rowChecked[i] = false; } Invalidate(); } } void TableView::SetCellBorderSize(int size) { m_cellBorderSize = (std::max)(0, size); Invalidate(); } void TableView::SetCellBorderStyle(StrokeStyle style) { m_cellBorderStyle = style; Invalidate(); } void TableView::SetCellBorderColor(const Color& color) { m_cellBorderColor = color; Invalidate(); } void TableView::SetCellBackColor(const Color& color) { m_cellBackColor = color; Invalidate(); } void TableView::SetCellForeColor(const Color& color) { m_cellForeColor = color; Invalidate(); } void TableView::SetCellFontSize(int size) { m_cellFontSize = (std::max)(8, size); // 同步更新编辑框字体 m_editBox->Style.FontSize = m_cellFontSize; // 更新所有行高 for (int i = 0; i < (int)m_data.size(); ++i) { UpdateRowHeight(i); } } void TableView::SetCellFontFamily(const std::wstring& fontFamily) { m_cellFontFamily = fontFamily; // 同步更新编辑框字体 m_editBox->Style.FontFamily = m_cellFontFamily; // 更新所有行高 for (int i = 0; i < (int)m_data.size(); ++i) { UpdateRowHeight(i); } } void TableView::SetCellStyle(int row, int col, const CellStyle& style) { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { m_data[row][col].Style = style; Invalidate(); } } CellStyle TableView::GetCellStyle(int row, int col) const { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { return m_data[row][col].Style; } return CellStyle(); } void TableView::ResetCellStyle(int row, int col) { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { m_data[row][col].Style.Reset(); Invalidate(); } } void TableView::SetHeaderBackColor(const Color& color) { m_headerBackColor = color; Invalidate(); } void TableView::SetHeaderForeColor(const Color& color) { m_headerForeColor = color; Invalidate(); } void TableView::SetDefaultTextAlign(TextAlign align) { m_cellTextAlign = align; Invalidate(); } void TableView::SetColumnTextAlign(int colIndex, TextAlign align) { if (colIndex >= 0 && colIndex < (int)m_columns.size()) { m_columns[colIndex].CellTextAlign = align; Invalidate(); } } void TableView::SetRowTextAlign(int rowIndex, TextAlign align) { // 自动扩展 while (rowIndex >= (int)m_rowTextAlign.size()) { m_rowTextAlign.push_back(m_cellTextAlign); // 使用默认值 } if (rowIndex >= 0 && rowIndex < (int)m_rowTextAlign.size()) { m_rowTextAlign[rowIndex] = align; Invalidate(); } } void TableView::SetCellTextAlign(int row, int col, TextAlign align) { if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { m_data[row][col].Style.SetTextAlign(align); Invalidate(); } } TextAlign TableView::GetCellTextAlign(int row, int col) const { // 优先级:单元格 > 行 > 列 > 默认 if (row >= 0 && row < (int)m_data.size() && col >= 0 && col < (int)m_data[row].size()) { // 单元格级别 if (m_data[row][col].Style.HasTextAlign) { return m_data[row][col].Style.Align; } } // 行级别(只有当行索引在 m_rowTextAlign 范围内才使用) if (row >= 0 && row < (int)m_rowTextAlign.size()) { // 这里应该检查是否真的设置过,但为了兼容性,我们假设设置过就使用 // 如果数组已扩展,就使用该行的对齐方式 return m_rowTextAlign[row]; } // 列级别 if (col >= 0 && col < (int)m_columns.size()) { return m_columns[col].CellTextAlign; } // 默认值 return m_cellTextAlign; } int TableView::GetHoverRow() const { return m_hoverRow; } int TableView::GetHoverCol() const { return m_hoverCol; } void TableView::GetHoverCell(int* outRow, int* outCol) const { if (outRow) *outRow = m_hoverRow; if (outCol) *outCol = m_hoverCol; } const Size& TableView::GetContentSize() { // 计算内容大小供滚动条使用 m_contentSize.Width = GetContentWidth(); m_contentSize.Height = GetContentHeight(); return m_contentSize; } ScrollBar* TableView::GetVScrollBar() { return &m_vScrollBar; } ScrollBar* TableView::GetHScrollBar() { return &m_hScrollBar; } void TableView::SetAttribute(const UIString& key, const UIString& value) { __super::SetAttribute(key, value); // 辅助函数:解析整数 auto parseInt = [](const UIString& v) -> int { UIString tmp = v; ui_text::Replace(&tmp, "px", ""); tmp = tmp.trim(); return std::atoi(tmp.c_str()); }; // 辅助函数:解析颜色 auto parseColor = [](const UIString& v) -> Color { bool isGood = false; return Color::Make(v, &isGood); }; do { // 表头高度 if (key == "header-height" || key == "headerheight") { SetHeaderHeight(parseInt(value)); break; } // 第一列类型 if (key == "first-column-type" || key == "firstcolumntype") { if (value == "index" || value == "number") { SetFirstColumnType(FirstColumnType::Index); } else if (value == "checkbox" || value == "check") { SetFirstColumnType(FirstColumnType::CheckBox); } else { SetFirstColumnType(FirstColumnType::TextBox); } break; } // 第一列宽度 if (key == "first-column-width" || key == "firstcolumnwidth") { SetFirstColumnWidth(parseInt(value)); break; } // 默认行高 if (key == "default-row-height" || key == "defaultrowheight" || key == "row-height" || key == "rowheight") { SetDefaultRowHeight(parseInt(value)); break; } // 单元格边框大小 if (key == "cell-border-size" || key == "cellbordersize" || key == "border-size" || key == "bordersize") { SetCellBorderSize(parseInt(value)); break; } // 单元格边框样式 if (key == "cell-border-style" || key == "cellborderstyle" || key == "border-style" || key == "borderstyle") { if (value == "solid") { SetCellBorderStyle(StrokeStyle::Solid); } else if (value == "dash" || value == "dashed") { SetCellBorderStyle(StrokeStyle::Dash); } else if (value == "none") { SetCellBorderStyle(StrokeStyle::None); } break; } // 单元格边框颜色 if (key == "cell-border-color" || key == "cellbordercolor" || key == "border-color" || key == "bordercolor") { SetCellBorderColor(parseColor(value)); break; } // 单元格背景颜色 if (key == "cell-back-color" || key == "cellbackcolor" || key == "cell-background" || key == "cellbackground") { SetCellBackColor(parseColor(value)); break; } // 单元格前景颜色(文字颜色) if (key == "cell-fore-color" || key == "cellforecolor" || key == "cell-color" || key == "cellcolor") { SetCellForeColor(parseColor(value)); break; } // 单元格字体大小 if (key == "cell-font-size" || key == "cellfontsize") { SetCellFontSize(parseInt(value)); break; } // 单元格字体 if (key == "cell-font-family" || key == "cellfontfamily" || key == "cell-font" || key == "cellfont") { SetCellFontFamily(value.unicode()); break; } // 表头背景颜色 if (key == "header-back-color" || key == "headerbackcolor" || key == "header-background" || key == "headerbackground") { SetHeaderBackColor(parseColor(value)); break; } // 表头前景颜色(文字颜色) if (key == "header-fore-color" || key == "headerforecolor" || key == "header-color" || key == "headercolor") { SetHeaderForeColor(parseColor(value)); break; } // 选中行背景颜色 if (key == "selected-row-back-color" || key == "selectedrowbackcolor" || key == "selected-color" || key == "selectedcolor") { SelectedRowBackColor = parseColor(value); Invalidate(); break; } // 表头文字(逗号分隔) if (key == "headers" || key == "columns") { auto headers = value.split(","); std::vector headerList; for (auto& h : headers) { headerList.push_back(h.trim()); } SetHeaders(headerList); break; } } while (false); } }