0

我在 C++ 中制作了一个控件,允许用户在具有不同位置的不规则框中选择一个位置,其中一些被阻塞,一些被填充,具有以下特征:

  • 可以在多行中创建多列(不一样,所以它可以有第一行有3列,第二行有1,第三行有5)

  • 每个位置的不同状态:阻塞、填充、空、选中(因此它可以同时具有其中一些状态:阻塞和空、阻塞和填充、空和选中)

  • 用户可以选择一个或多个位置

  • 它为每个填充或空白位置提供工具提示文本和不同的背景图像。

  • 添加/删除行/列的上下文菜单。

  • 行和列标题、数字/字母数字、升序/降序。

控件的结构是Container->(nRows x RowClass)和RowClass->(nCols x ColumnClass)。每列都包含一个 TableLayoutPanel,因此我可以模拟卡住和凸起的效果。

在网络上锁定不同的主题,我发现了一些想法,并且我为速度所做的改进基本上是 SuspendDrawing 和 SendMessage(在绘制它们之前创建和调整所有行和列的大小),双缓冲每个控件并添加一个 BeginEdit/ Container 的 EndEdit 方法,该方法在创建行和列时阻止行和列调整大小(每个 RowClass 都相同)。

对于相对较小的盒子,它的效果还不错,比如说 20 行 x 20 列,但是当它有 40 行 x 30 列(1.200 TableLayoutPanel's)时,即使在快速计算机中它也会变得非常慢。

我还尝试为每个 RowClass 使用一个 TableLayoutPanel(具有所需的列数),但问题是如何绘制边框以便分别选择每一列、每个单元格的工具提示和背景图像。

所以,问题是:我可以尝试任何改进吗?

我一直在想像魔兽争霸或类似的策略游戏,它们在屏幕上的速度很好,有成千上万的图形和计算,但我不是专业程序员,所以我不知道这可能是技术,如果它是正确的方式..

也许它可能是像一排图像的图像,或者不同于一类类的东西,但我不知道......

可能这是一个设计问题,所以任何关于如何使用这些规范创建快速控件的想法都会很棒。

无论如何感谢您阅读我的问题,任何评论都将受到欢迎!米格尔

4

3 回答 3

0

电子游戏至少在 GPU 上卸载了图形表示(专门为此目的而构建)。我不认为盒子的表示是瓶颈,而是到达那里的计算/步骤。发布您的代码/代码片段或解释您对算法的使用,因为当输入增加时,糟糕的算法会使您的程序非常慢 - 看看计算复杂性,算法课程提出的东西。 http://en.wikipedia.org/wiki/Computational_complexity_theory

于 2010-12-23T18:09:55.083 回答
0

对此没有干净的解决方法,您将不得不放弃 TLP。控件是一个非常昂贵的对象,任何拥有超过一百个控件的形式都会吸泥。

首先看一下支持网格状显示的内置控件类型。ListView with View = Details 用于不可编辑的网格,DataGridView 用于可编辑的网格。如果这不符合您的要求,那么您将不得不自己做。您需要实现的关键是 OnPaint() 方法来绘制控件的视觉外观,以及 OnMouseDown 或 OnClick 来进行命中测试和实现行为。

于 2010-12-23T18:19:33.023 回答
0

这是代码的一部分(整个代码真的很大),其中完成了一些计算..

在 ControlClass::

    void BeginEdit(int nFilas) {
        if (!_editando) {

            if (_muestraBarraProgreso) {
                this->BarraProgreso->Value = 0;
                this->BarraProgreso->Visible = true;
                }

            _editando = true;
            _lastEdit = nFilas;
            this->Cursor = Cursors::WaitCursor;
            this->Refresh();
            SendMessageClass::SuspendDrawing();
            }
        };

    /// <summary>Provoca se realicen los cambios visuales no realizados desde que se
    /// llamó a BeginEdit, como this->Controls.Add(...), Ajustar los tamaños de los controles, ...</summary>
    void EndEdit(void) {
        if (_editando) {
            int f;
            _editando = false;

            AjustaHeaders();
            AjustaTamaños(); //Provoca el ajuste de las filas sin ajustar las columnas

            //Esto lo hago para minimizar el número de AjustaTamaños()
            for (f=_lastEdit; f<_nFilas; f++)
                _fila[f]->EndEdit(false); //Provoca el ajuste de las columnas

            this->MuestraBarraProgreso = false;
            this->Cursor = Cursors::Default;
            SendMessageClass::ResumeDrawing();
            }
        };

    void AjustaTamaños(void) {
        if (!_editando && _nFilas>0) {
            int topOffset = 0, leftOffset = 0, bottomOffset = 0;

            if (_showColumnHeaders) topOffset = this->tlpColumnHeaders->Height;
            if (_showRowHeaders) leftOffset = this->tlpRowHeaders->Width;
            if (_muestraEstado) bottomOffset = this->EstadoRecipiente->Height;

            int hOffset = leftOffset + 2;
            int vOffset = topOffset + bottomOffset + 2;

            int i, j = 0, stp;

            if (_varsCom->ordenFilas == TipoOrdenFilas::Descendiente) {
                j = 0; stp = 1; 
                }
            else {
                j = _nFilas-1; stp = -1;
                }

            for (i=0; i<_nFilas; i++) {
                _fila[j]->Left = hOffset;
                _fila[j]->Width = this->Width - hOffset;
                _fila[j]->Top = (_nFilas - 1 - i) * (this->Height - vOffset) / _nFilas + topOffset;
                _fila[j]->Height = (this->Height - vOffset) / _nFilas;
                j+=stp;
                }
            }
        };

在 FilaClass 中:

    /// <summary>Provoca que no se realicen los cambios visuales hasta que se llame EndEdit</summary>
    void BeginEdit(void) {
        if (!_editando) {
            _editando = true;
            _lastEdit = _nColumnas;
            SendMessageClass::SuspendDrawing();
            }
        };
    /// <summary>Provoca se realicen los cambios visuales no realizados desde que se
    /// llamó a BeginEdit, como this->Controls.Add(...), Ajustar los tamaños de los controles, ...</summary>
    void EndEdit(bool resumirDrawing) {
        if (_editando) {
            _editando = false;

            AjustaTamaños();

            for (int c=_lastEdit; c<_nColumnas;c++)
                _columna[c]->EndEdit(resumirDrawing);

            if (resumirDrawing) SendMessageClass::ResumeDrawing();
            }
        };

    void AjustaTamaños(void) {
        if (!_editando) {
            int j, stp;

            if (_varsCom->ordenColumnas == TipoOrdenColumnas::IzquierdaDerecha) {
                j = 0; stp = 1;
                }
            else {
                j = _nColumnas-1; stp = -1;
                }

            for (int i=0; i<_nColumnas; i++) {
                _columna[j]->Width = this->Width / _nColumnas;
                _columna[j]->Left = i * this->Width / _nColumnas;
                _columna[j]->Top = 0;
                _columna[j]->Height = this->Height;
                j+=stp;
                }
            }
        };

在 Columna 类中:

    void BeginEdit(void) {
        _editando = true;
        SendMessageClass::SuspendDrawing();
        };
    void EndEdit(bool resumirDrawing) {
        if (_editando) {
            _editando = false;
            _CambiaEstado = _estado;
            if (resumirDrawing) SendMessageClass::ResumeDrawing();
            }
        };
internal:       
    /// <summary>Obtiene o establece el estado de la posición sin activar el evento ColumnaChange</summary>
    property EstadoColumna _CambiaEstado {
        EstadoColumna get() { return _estado; }
        void set(EstadoColumna value) {
            _estado = value;
            if ( (_varsCom->bloquearLlenas && ((value & EstadoColumna::llena) == EstadoColumna::llena)) || 
                 (_varsCom->bloquearVacias && ((value & EstadoColumna::llena) != EstadoColumna::llena)) ) {
                if ((_estado & EstadoColumna::seleccionada) == EstadoColumna::seleccionada)
                    _estado = (EstadoColumna) (_estado ^ EstadoColumna::seleccionada);
                _estado = (EstadoColumna) (_estado | EstadoColumna::bloqueada);
                }
            else {
                if ((_estado & EstadoColumna::bloqueada) == EstadoColumna::bloqueada)
                    _estado = (EstadoColumna) (_estado | EstadoColumna::bloqueada);
                }

            if (!_editando) {
                ActualizaEstado();
                ActualizaToolTip();
                }
            }
        };

    /// <summary>Actualiza la visualización de la posición leyendo de nuevo los parámetros</summary>
    void ActualizaEstado(void) {
        if (!_editando && _varsCom->estilo) {
            this->ColumnaCtl->BackColor = _varsCom->estilo->GetEstilo[(int) _estado]->colorFondo;
            this->ColumnaCtl->CellBorderStyle = _varsCom->estilo->GetEstilo[(int) _estado]->bordeCelda;
            this->ColumnaCtl->BackgroundImageLayout = _varsCom->imageLayout;

            if (this->Estado[EstadoColumna::llena])
                this->ColumnaCtl->BackgroundImage = _varsCom->imagenLlena;
            else if (this->Estado[EstadoColumna::vacia])
                this->ColumnaCtl->BackgroundImage = _varsCom->imagenVacia;
            }
        };

    /// <summary>Actualiza los textos del ToolTipText de la posición</summary>
    void ActualizaToolTip(void) {
        if (!_editando && _varsCom->etiquetas) {
            String^ sTmp = "";
            int __tmp__ = 0;

            if (_varsCom->tipoHeaderFilas == TipoHeader::Numerica)
                sTmp = _varsCom->etiquetas->Fila + ": " + _nombreFila;
            else if (System::Int32::TryParse(_nombreFila, __tmp__))
                sTmp = _varsCom->etiquetas->Fila + ": " + gcnew String((wchar_t) ('A' + __tmp__ - 1), 1);
            else 
                sTmp = _varsCom->etiquetas->Fila + ": " + _nombreFila;

            if (_varsCom->tipoHeaderColumnas == TipoHeader::Numerica)
                sTmp += ", " + _varsCom->etiquetas->Columna + ": " + _posicion + "\n";
            else
                sTmp += ", " + _varsCom->etiquetas->Columna + ": " + gcnew String((wchar_t) ('A' + _posicion - 1), 1) + "\n";

            if (_nombre != "") {
                if ((_estado & EstadoColumna::llena) == EstadoColumna::llena)
                    sTmp += _varsCom->etiquetas->Posicion + ": " + _nombre + "\n";
                else
                    sTmp += _varsCom->etiquetas->PosicionVacia + ": " + _nombre + "\n";
                }

            if ((_estado & EstadoColumna::llena) == EstadoColumna::llena) sTmp += "Ocupada, ";
            else sTmp += "Vacía, ";

            if ((_estado & EstadoColumna::seleccionada) != EstadoColumna::vacia) sTmp += "Seleccionada";
            else sTmp += "Sin seleccionar";

            this->ToolTip1->ShowAlways = false;
            this->ToolTip1->SetToolTip(this->ColumnaCtl, sTmp);
            this->ToolTip1->ShowAlways = true;
            }
        };

我认为这些是计算的大部分内容.. 有点未注释,并且是西班牙语,但任何帮助都会非常有用!不管怎么说,还是要谢谢你!

于 2010-12-24T18:19:54.737 回答