使用动态内容刷新wxGrid

时间:2012-01-13 21:42:16

标签: c++ wxwidgets

这似乎并不像我希望的那么简单:

使用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上看到相同的行为。

3 个答案:

答案 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 ++版本的在线示例,但是,使用这两个库我可以告诉你它们都以相同的方式使用。