0

我创建了一个 wxGrid,我有 3 行和 2 列。我想在同一个单元格中添加 wxTextCtrl 和 wxButton。

pGrid->SetCellValue(3,0,"Label");
wxBoxSizer *hSizer = new wxBoxSizer(wxHORIZONTAL);
wxTextCtrl *txt = new wxTextCtrl(this, ID, "");
wxButton *btn = new wxButton(this, ID2, "OK");
hSizer->Add(txt);
hSizer->Add(btn);
pGrid->SetCellRenderer(3,1, hSizer);
4

2 回答 2

2

您需要为此创建一个自定义编辑器,显示您自己的自定义复合窗口,其中包含一个文本控件和一个按钮。

于 2021-01-08T15:44:28.907 回答
1

这是一个从 wxGridCellTextEditor 类中剪切和粘贴 95% 的示例。

static wxRect AdjustRectForPlatform(const wxRect& rectOrig)
{
    wxRect rect(rectOrig);

//    // Make the edit control large enough to allow for internal margins
//    //
//    // TODO: remove this if the text ctrl sizing is improved esp. for unix
//    //
#if defined(__WXGTK__)
    if (rect.x != 0)
    {
        rect.x += 1;
        rect.y += 1;
        rect.width -= 1;
        rect.height -= 1;
    }
#elif defined(__WXMSW__)
    if ( rect.x == 0 )
        rect.x += 2;
    else
        rect.x += 3;

    if ( rect.y == 0 )
        rect.y += 2;
    else
        rect.y += 3;

    rect.width -= 2;
    rect.height -= 2;
#elif defined(__WXOSX__)
    rect.x += 1;
    rect.y += 1;

    rect.width -= 1;
    rect.height -= 1;
#else
    int extra_x = ( rect.x > 2 ) ? 2 : 1;
    int extra_y = ( rect.y > 2 ) ? 2 : 1;

    #if defined(__WXMOTIF__)
        extra_x *= 2;
        extra_y *= 2;
    #endif

    rect.SetLeft( wxMax(0, rect.x - extra_x) );
    rect.SetTop( wxMax(0, rect.y - extra_y) );
    rect.SetRight( rect.GetRight() + 2 * extra_x );
    rect.SetBottom( rect.GetBottom() + 2 * extra_y );


#endif

    return rect;
}

wxDEFINE_EVENT(gridEVT_BUTTON, wxCommandEvent);

class wxGridCellStringAndButtonRenderer: public wxGridCellStringRenderer
{
    public:
    wxGridCellStringAndButtonRenderer():wxGridCellStringRenderer(){}
    virtual void Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc,
                      const wxRect &rect, int row, int col, bool isSelected)
    {
        dc.SetBackground(attr.GetBackgroundColour());
        dc.Clear();

        // Draw the text.
        wxRect adjRect = AdjustRectForPlatform(rect);

        int buttonHeight = adjRect.GetHeight();
        wxRect textRect = rect;
        textRect.SetWidth(rect.GetWidth() - buttonHeight);

        wxGridCellStringRenderer::Draw(grid, attr, dc, textRect,
                                       row, col, isSelected);

        // Draw the button.
        wxRendererNative& renderer = wxRendererNative::GetDefault();

        const int buttonLeft = rect.GetLeft()+rect.GetWidth()-buttonHeight;
        const int buttonTop = adjRect.GetTop()-1;
        wxSize buttonSize = wxSize(buttonHeight,buttonHeight);

        wxRect rect3 = wxRect(wxPoint(buttonLeft,buttonTop),buttonSize);
        renderer.DrawPushButton(&grid,dc,rect3);
    }
};

class wxGridCellTextAndButtonEditor : public wxGridCellEditor
{
public:
    explicit wxGridCellTextAndButtonEditor(size_t maxChars = 0);

    virtual void Create(wxWindow* parent,
                        wxWindowID id,
                        wxEvtHandler* evtHandler) wxOVERRIDE;
    virtual void SetSize(const wxRect& rect) wxOVERRIDE;

    virtual void PaintBackground(wxDC& dc,
                                 const wxRect& rectCell,
                                 const wxGridCellAttr& attr) wxOVERRIDE;

    virtual bool IsAcceptedKey(wxKeyEvent& event) wxOVERRIDE;
    virtual void BeginEdit(int row, int col, wxGrid* grid) wxOVERRIDE;
    virtual bool EndEdit(int row, int col, const wxGrid* grid,
                         const wxString& oldval, wxString *newval) wxOVERRIDE;
    virtual void ApplyEdit(int row, int col, wxGrid* grid) wxOVERRIDE;

    virtual void Reset() wxOVERRIDE;
    virtual void StartingKey(wxKeyEvent& event) wxOVERRIDE;
    virtual void HandleReturn(wxKeyEvent& event) wxOVERRIDE;

    // parameters string format is "max_width"
    virtual void SetParameters(const wxString& params) wxOVERRIDE;
#if wxUSE_VALIDATORS
    virtual void SetValidator(const wxValidator& validator);
#endif

    virtual wxGridCellEditor *Clone() const wxOVERRIDE;

    // added GetValue so we can get the value which is in the control
    virtual wxString GetValue() const wxOVERRIDE;

protected:
    // parts of our virtual functions reused by the derived classes
    void DoCreate(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler,
                  long style = 0);
    void DoBeginEdit(const wxString& startValue);
    void DoReset(const wxString& startValue);

    void OnButton(wxCommandEvent&);

private:
    size_t                   m_maxChars;        // max number of chars allowed
#if wxUSE_VALIDATORS
    wxScopedPtr<wxValidator> m_validator;
#endif
    wxString                 m_value;

    wxDECLARE_NO_COPY_CLASS(wxGridCellTextAndButtonEditor);

    wxEvtHandler* m_handler;
    wxTextCtrl* m_text;
    wxButton* m_button;
};

wxGridCellTextAndButtonEditor::wxGridCellTextAndButtonEditor(size_t maxChars)
{
    m_maxChars = maxChars;
}

void wxGridCellTextAndButtonEditor::Create(wxWindow* parent,
                                  wxWindowID id,
                                  wxEvtHandler* evtHandler)
{
    DoCreate(parent, id, evtHandler);
}

void wxGridCellTextAndButtonEditor::OnButton(wxCommandEvent&)
{
    wxCommandEvent event(gridEVT_BUTTON);
    m_handler->ProcessEvent(event);

    m_text->SetFocus();
}

void wxGridCellTextAndButtonEditor::DoCreate(wxWindow* parent,
                                    wxWindowID id,
                                    wxEvtHandler* evtHandler,
                                    long style)
{
    wxControl* control = new wxControl(parent, id, wxDefaultPosition,
                                       wxDefaultSize, wxNO_BORDER);
    m_handler = evtHandler;

    style |= wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxNO_BORDER;
    m_text = new wxTextCtrl(control, wxID_ANY, wxEmptyString,
                                            wxDefaultPosition, wxDefaultSize,
                                            style);
    m_text->SetMargins(0, 0);

    m_button = new wxButton(control,wxID_ANY,wxString());
    m_button->Bind(wxEVT_BUTTON,&wxGridCellTextAndButtonEditor::OnButton,this);

    m_control = control;

    wxBoxSizer* szr = new wxBoxSizer(wxHORIZONTAL);
    szr->Add(m_text,wxSizerFlags(1));
    szr->Add(m_button,wxSizerFlags());

    control->SetSizer(szr);

#ifdef __WXOSX__
    wxWidgetImpl* impl = m_text->GetPeer();
    impl->SetNeedsFocusRect(false);
#endif
    // set max length allowed in the textctrl, if the parameter was set
    if ( m_maxChars != 0 )
    {
        m_text->SetMaxLength(m_maxChars);
    }
#if wxUSE_VALIDATORS
    // validate text in textctrl, if validator is set
    if ( m_validator )
    {
        m_text->SetValidator(*m_validator);
    }
#endif

    wxGridCellEditor::Create(parent, id, evtHandler);
}

void wxGridCellTextAndButtonEditor::PaintBackground(wxDC& dc,
                                           const wxRect& WXUNUSED(rectCell),
                                           const wxGridCellAttr& WXUNUSED(attr))
{
    // as we fill the entire client area,
    // don't do anything here to minimize flicker
}

void wxGridCellTextAndButtonEditor::SetSize(const wxRect& rectOrig)
{
    wxRect rect = AdjustRectForPlatform(rectOrig);

    m_button->SetMinSize(wxSize(rect.GetHeight(),rect.GetHeight()));
    m_button->SetMaxSize(wxSize(rect.GetHeight(),rect.GetHeight()));
    wxGridCellEditor::SetSize(rect);
}

void wxGridCellTextAndButtonEditor::BeginEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));

    m_value = grid->GetTable()->GetValue(row, col);

    DoBeginEdit(m_value);
}

void wxGridCellTextAndButtonEditor::DoBeginEdit(const wxString& startValue)
{
    m_text->SetValue(startValue);
    m_text->SetInsertionPointEnd();
    m_text->SelectAll();
    m_text->SetFocus();

    m_control->Layout();
}

bool wxGridCellTextAndButtonEditor::EndEdit(int WXUNUSED(row),
                                   int WXUNUSED(col),
                                   const wxGrid* WXUNUSED(grid),
                                   const wxString& WXUNUSED(oldval),
                                   wxString *newval)
{
    wxCHECK_MSG( m_control, false,
                 "wxGridCellTextAndButtonEditor must be created first!" );

    const wxString value = m_text->GetValue();
    if ( value == m_value )
        return false;

    m_value = value;

    if ( newval )
        *newval = m_value;

    return true;
}

void wxGridCellTextAndButtonEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
    grid->GetTable()->SetValue(row, col, m_value);
    m_value.clear();
}

void wxGridCellTextAndButtonEditor::Reset()
{
    wxASSERT_MSG( m_control, "wxGridCellTextAndButtonEditor must be created first!" );

    DoReset(m_value);
}

void wxGridCellTextAndButtonEditor::DoReset(const wxString& startValue)
{
    m_text->SetValue(startValue);
    m_text->SetInsertionPointEnd();
}

bool wxGridCellTextAndButtonEditor::IsAcceptedKey(wxKeyEvent& event)
{
    switch ( event.GetKeyCode() )
    {
        case WXK_DELETE:
        case WXK_BACK:
            return true;

        default:
            return wxGridCellEditor::IsAcceptedKey(event);
    }
}

void wxGridCellTextAndButtonEditor::StartingKey(wxKeyEvent& event)
{
    // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
    // longer an appropriate way to get the character into the text control.
    // Do it ourselves instead.  We know that if we get this far that we have
    // a valid character, so not a whole lot of testing needs to be done.

    int ch;

    bool isPrintable;

#if wxUSE_UNICODE
    ch = event.GetUnicodeKey();
    if ( ch != WXK_NONE )
        isPrintable = true;
    else
#endif // wxUSE_UNICODE
    {
        ch = event.GetKeyCode();
        isPrintable = ch >= WXK_SPACE && ch < WXK_START;
    }

    switch (ch)
    {
        case WXK_DELETE:
            // Delete the initial character when starting to edit with DELETE.
            m_text->Remove(0, 1);
            break;

        case WXK_BACK:
            // Delete the last character when starting to edit with BACKSPACE.
            {
                const long pos = m_text->GetLastPosition();
                m_text->Remove(pos - 1, pos);
            }
            break;

        default:
            if ( isPrintable )
                m_text->WriteText(static_cast<wxChar>(ch));
            break;
    }
}

void wxGridCellTextAndButtonEditor::HandleReturn( wxKeyEvent& event )
{
#if defined(__WXMOTIF__) || defined(__WXGTK__)
    // wxMotif needs a little extra help...
    size_t pos = (size_t)( Text()->GetInsertionPoint() );
    wxString s( Text()->GetValue() );
    s = s.Left(pos) + wxT("\n") + s.Mid(pos);
    Text()->SetValue(s);
    Text()->SetInsertionPoint( pos );
#else
    // the other ports can handle a Return key press
    //
    event.Skip();
#endif
}

void wxGridCellTextAndButtonEditor::SetParameters(const wxString& params)
{
    if ( !params )
    {
        // reset to default
        m_maxChars = 0;
    }
    else
    {
        long tmp;
        if ( params.ToLong(&tmp) )
        {
            m_maxChars = (size_t)tmp;
        }
        else
        {
            wxLogDebug( wxT("Invalid wxGridCellTextAndButtonEditor parameter string '%s' ignored"), params.c_str() );
        }
    }
}

#if wxUSE_VALIDATORS
void wxGridCellTextAndButtonEditor::SetValidator(const wxValidator& validator)
{
    m_validator.reset(static_cast<wxValidator*>(validator.Clone()));
}
#endif

wxGridCellEditor *wxGridCellTextAndButtonEditor::Clone() const
{
    wxGridCellTextAndButtonEditor* editor = new wxGridCellTextAndButtonEditor(m_maxChars);
#if wxUSE_VALIDATORS
    if ( m_validator )
    {
        editor->SetValidator(*m_validator);
    }
#endif
    return editor;
}

// return the value in the text control
wxString wxGridCellTextAndButtonEditor::GetValue() const
{
    return m_text->GetValue();
}

在 Windows 上,它看起来像这样:

在此处输入图像描述

此代码在单击时引发一个从 wxCommandEvent 派生的事件,类型为 gridEVT_BUTTON。它不包含任何其他信息,但如果需要,可以调整代码以包含行和列(例如在事件的 Int 和 ExtraLong 字段中)。

于 2021-01-09T03:44:38.243 回答