这似乎并不像我希望的那么简单:
使用wxWidgets(2.8稳定系列),我有一个wxGrid(不是子类),自定义“数据适配器”作为wxGridTableBase派生类。
wxGrid* grid = new wxGrid (this, ID_TABLE);
grid->SetTable (new TableAdapter (foo, bar, baz));
grid->EnableEditing (false);
sizer->Add(grid, wxSizerFlags (1).Expand());
我找不到的“简单”的东西是在底层数据模型发生变化时刷新网格的一种方法。简单地调用wxWindow :: Update(pGrid->Update()
)显然不足以让网格调用底层的wxGridTableBase实现?
wxGrid* const grid = (wxGrid* const) FindWindow (ID_TABLE);
if (NULL != grid) {
grid->Update ();
grid->AutoSizeColumns ();
}
特别是,此网格充当列表,并且将通过相同或(可能)另一个进程异步添加和删除行 - 它是一个共享数据列表,可以由几个中的任何一个更新网络系统。网格/列表本身实际上是只读的;其他控件用于添加和删除项目,每行都有一个可以切换的布尔类型属性。
似乎新行未添加到视图中,删除行将导致wx绘图代码中的间歇性SEGV。
由于动态/异步更新机制,我希望避免不断删除并重新添加网格到窗口,因为我确信会导致各种各样的闪烁和肮脏...所以,如果我绝对必须的话,我会再次尝试类似暴力的东西,但我更倾向于避免它。
不幸的是,尽管被标记为“稳定版本”,但wxGrid文档似乎主要由Yet to be written
标记组成。
更新
我开始怀疑这是一个容器布局问题。在绘制网格时,网格的底部(最后一行)实际上可以围绕wxFrame窗口部分的wxStaticBox框架以及框架状态行的一部分重叠。添加和删除行似乎不会强制重新布局容器;我正在尝试调用Layout
之类的东西。理想情况下,这应该是一个滚动区域,但wxGrid 应仍然在其包含的Sizer中“约束”。
布局实际上包含一个静态框,其中包含一个垂直框,其中第一个元素是一个水平的按钮框,然后是网格,如下所示:
--[ Static Box ]------------------------
| |
| [Button] [Button] [Button] |
| |
| ----------------------------------- |
| | | A | B | C | |
| |-----------------------------------| |
| | 1 | 1a | 1b | 1c | |
| ----------------------------------- |
| |
----------------------------------------
不幸的是,公司政策禁止我发布截图: - (
如果重要的话,这是(目前)Fedora 16(x86_64)上的wxGTK-2.8.12,虽然我在使用EPEL(Fedora)软件包的CentOS5 / RHEL5上看到相同的行为。
答案 0 :(得分:7)
经过多次实验,强制刷新的“正确”方式看起来像这样:
bool
CDynamicWxGridTable::AppendRows(const size_t IGNORED _)
{
wxGrid *grid = GetView();
if (pGrid != NULL)
{
const int iNumRecords = GetNumberRows();
const int iGridRows = grid->GetNumberRows();
const int iNeedRows = iNumRecords - iGridRows;
if (iNeedRows)
{
grid->BeginBatch();
grid->ClearSelection();
if (grid->IsCellEditControlEnabled())
{
grid->DisableCellEditControl();
}
{
wxGridTableMessage pop(this,
wxGRIDTABLE_NOTIFY_ROWS_DELETED,
0, iGridRows);
grid->ProcessTableMessage(pop);
}
{
wxGridTableMessage push(this,
wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
iNumRecords);
grid->ProcessTableMessage(push);
}
grid->AutoSize();
grid->ForceRefresh();
grid->EndBatch();
}
}
return true;
}
bool
CDynamicWxGridTable::DeleteRows(const size_t IGNORED pos,
const size_t IGNORED rows)
{
return AppendRows(0);
}
这些是在我的(5Hz)更新例程期间通过抓取grid
并调用其 ->AppendRows(1)
方法调用的,后者又调用wxTableBase
派生类::AppendRows
成员。
不幸的是,由于我是从异步的,动态更新的记录中提取的,因此对于行属性,wxGrid缓存系统仍在“对抗”我(如果行更改,那么其GetAttr
值应该更改,它不会动态刷新,因为上面只测试应该有的行数与实际存在的行数。然而,这是一个相对较小的错误,我希望通过其他方式克服它。
“关键”部分似乎是通过ProcessTableMessage
将删除/追加行消息合成到wxGridTable系统...没有这些消息,wxGrid缓存似乎没有注意到表大小的变化。
顺便提一下,通过在::GetValue(const int row, const int column)
方法中放置警卫来检查有效值,可以减轻因丢失行而导致的崩溃:
if (row < 0 || row > GetNumberRows()) { return L"×"; }
if (col < 0 || col > LAST_COLUMN) { return L"×"; }
然而,在添加上面的疯狂消息注入逻辑之后,似乎无法显示这些“×”值。
答案 1 :(得分:1)
wxGrid :: ForceRefresh()
立即重新绘制网格。使用它代替通常的wxWindow :: Refresh。
答案 2 :(得分:1)
不幸的是,调用pGrid->Update()
似乎不够。对该函数的调用实际上会调用wxWindow::Update
,它会重新绘制窗口的无效区域,以及递归的所有子节点,它可能无法正常工作。
相反,您要调用的内容为wxGrid::ForceRefresh()
,详见文档here。文档说
立即重新绘制网格。
使用此代替通常的
wxWindow::Refresh()
。
有趣的是,如果你看一下wxWidgets提供的样本项目中的网格示例,他们只使用wxGrid::Refresh
。
我使用过wxWidgets(C ++)和wxPython,对于我的wxGrid,我使用了ForceRefresh。可以找到使用它的一个示例here。虽然它是wxPython,但我似乎没有找到使用C ++版本的在线示例,但是,使用这两个库我可以告诉你它们都以相同的方式使用。