Custom combobox grid editor to default to selected items Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
RichardW
Earned a small fee
Earned a small fee
Posts: 14
Joined: Fri Apr 25, 2014 4:45 am

Custom combobox grid editor to default to selected items

Post by RichardW »

I've built a simple combobox grid editor to store data in a key -> value way, using wxStringClientData to store the key. Everything is working, however I want to hide the key by having the comboboxes open immediately upon drawing the grid.

Here is what it looks like when first opened:
Not showing boxes.
Not showing boxes.
img1.jpg (5.82 KiB) Viewed 2692 times
By sticking a Show() in BeginEdit(), clicking on each one gives me what I want:
Showing boxes.
Showing boxes.
img2.jpg (7.57 KiB) Viewed 2692 times
It looks like Create() is not called right away. Rather, it's only called when interacted with. (ie. left-click)

I've tried doing things like this:

Code: Select all

wxGridEvent Ev(pogrid->GetId(), wxEVT_GRID_SELECT_CELL,pogrid,row,col);
pogrid->GetEventHandler()->ProcessEvent(Ev);
which does nothing. I've tried doing a customcontrol->GetControl()->Command(mywxCommandEvent). That just crashes everything.

Does anyone have any ideas how to either:
1. Get the comboboxes to be Create()d during construction?
2. Use an event to force the comboboxes to be generated?

Thanks for any help you can give. I've included the source code but you can also refer to the EnumEditor. It behaves the same way as mine. Mostly because that's where I got most of the code from. :)

Code: Select all

mywidgets.h

class DBComboBox : public wxComboBox {
    public:
    DBComboBox(wxWindow *parent, wxWindowID id,wxString defaultselection,const wxPoint pos, const wxSize size, int style);
    void Prime(const wxArrayString *charray,wxArrayString *dataarray);
    wxString getid();
    void setbyid(wxString id);
};

class WXDLLIMPEXP_ADV DBGridCellChoiceEditor : public wxGridCellChoiceEditor
{
public:
    DBGridCellChoiceEditor( const wxArrayString& choices,const wxArrayString &ids );
    virtual ~DBGridCellChoiceEditor() {}
    virtual void Create(wxWindow* parent,wxWindowID id,wxEvtHandler* evtHandler);

    virtual wxGridCellEditor*  Clone() const;

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

protected:
    wxArrayString m_choices;
    wxArrayString m_ids;
private:
    wxString m_index;
    wxDECLARE_NO_COPY_CLASS(DBGridCellChoiceEditor);
};

Code: Select all

mywidgets.cpp

DBComboBox::DBComboBox(wxWindow *parent, wxWindowID id, wxString defaultselection, const wxPoint pos, const wxSize size, int style) :
      wxComboBox(parent,id,defaultselection,pos,size,0,NULL,style,wxDefaultValidator,wxEmptyString) { }
    
    
void DBComboBox::Prime(const wxArrayString *charray,wxArrayString *dataarray) {
    Clear();
    for (int i=0;i<charray->GetCount();++i) {
        Append(charray->Item(i),new wxStringClientData(dataarray->Item(i)));
    }
}
    
wxString DBComboBox::getid() {
    int pos;
    pos=GetSelection();
    if (pos==wxNOT_FOUND) return "0";
    return ((wxStringClientData*)GetClientObject(pos))->GetData();
}
    
    // Selects the item in the listbox.
    // Useful after the listbox has been primed.
void DBComboBox::setbyid(wxString id) {
    unsigned int pos;
    if (id.IsSameAs("0")) {
        SetSelection(0);
        return;
    }
    
    for (pos=0;pos<GetCount();++pos) {
        if (id.IsSameAs(((wxStringClientData*)GetClientObject(pos))->GetData()))
            SetSelection(pos);
    }
}


DBGridCellChoiceEditor::DBGridCellChoiceEditor(const wxArrayString& choices,const wxArrayString &ids)
                     :wxGridCellChoiceEditor(),m_choices(choices),m_ids(ids)
{
    m_index = "0";
}

wxGridCellEditor *DBGridCellChoiceEditor::Clone() const
{
    DBGridCellChoiceEditor *editor = new DBGridCellChoiceEditor(m_choices,m_ids);
    return editor;
}

void DBGridCellChoiceEditor::Create(wxWindow* parent,wxWindowID id,wxEvtHandler* evtHandler)
{
    int style = wxTE_PROCESS_ENTER |
                wxTE_PROCESS_TAB |
                wxCB_READONLY |
                wxBORDER_NONE;
    int i;
    
    m_control = new DBComboBox(parent, id, wxEmptyString,
                               wxDefaultPosition, wxDefaultSize,
                               style);
                               
    for (i=0;i<m_ids.size();++i)
        ((DBComboBox*)m_control)->Append(m_choices.Item(i),new wxStringClientData(m_ids.Item(i)));
        
    wxGridCellEditor::Create(parent, id, evtHandler);
    
}

void DBGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control,
                 wxT("The DBGridCellChoiceEditor must be Created first!"));
    wxGridCellEditorEvtHandler* evtHandler = NULL;
    if (m_control)
        evtHandler = wxDynamicCast(m_control->GetEventHandler(), wxGridCellEditorEvtHandler);

    // Don't immediately end if we get a kill focus event within BeginEdit
    if (evtHandler)
        evtHandler->SetInSetFocus(true);

    wxGridTableBase *table = grid->GetTable();

    m_index=table->GetValue(row,col);
    if (m_index.empty()) m_index="0";
    SetById(m_index);
    
#ifdef __WXOSX_COCOA__
    // This is a work around for the combobox being simply dismissed when a
    // choice is made in it under OS X. The bug is almost certainly due to a
    // problem in focus events generation logic but it's not obvious to fix and
    // for now this at least allows to use wxGrid.
    Combo()->Popup();
#endif

    if (evtHandler)
    {
        // When dropping down the menu, a kill focus event
        // happens after this point, so we can't reset the flag yet.
#if !defined(__WXGTK20__)
        evtHandler->SetInSetFocus(false);
#endif
    }
}

bool DBGridCellChoiceEditor::EndEdit(int WXUNUSED(row),
                                   int WXUNUSED(col),
                                   const wxGrid* WXUNUSED(grid),
                                   const wxString& WXUNUSED(oldval),
                                   wxString *newval)
{

    wxString idx = ((DBComboBox*)m_control)->getid();
    Show(true);
    if ( idx.IsSameAs(m_index) )
        return false;

    m_index = idx;

    if ( newval )
        newval->Printf(m_index);

    return true;
}

void DBGridCellChoiceEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
    wxGridTableBase * const table = grid->GetTable();
    table->SetValue(row, col, m_index);
}

void DBGridCellChoiceEditor::SetById(wxString theid) {
    ((DBComboBox*)m_control)->setbyid(theid);
}
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Custom combobox grid editor to default to selected items

Post by doublemax »

The whole concept of wxGrid is based on the idea that only one cell editor exists at any given time.

The way to solve is would be to create a custom wxGridCellRenderer that mimics the look of a wxComboBox. You can use wxRendererNative::DrawComboBox() for that.
http://docs.wxwidgets.org/trunk/classwx ... 96fb7ddcd2

Check the implementation of wxGridCellBoolRenderer in src/generic/gridctrl.cpp. It does the same for a wxCheckBox.
Use the source, Luke!
RichardW
Earned a small fee
Earned a small fee
Posts: 14
Joined: Fri Apr 25, 2014 4:45 am

Re: Custom combobox grid editor to default to selected items

Post by RichardW »

doublemax wrote:The whole concept of wxGrid is based on the idea that only one cell editor exists at any given time.
Except... It doesn't look like that. What I want is *exactly* img2.jpg above. If I click on every row in column 1, I get what I want.

If I could simulate a leftclick for each one, I'll have what I want without clicking on everything. Is there any way to do that?
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Custom combobox grid editor to default to selected items

Post by doublemax »

Maybe you could try wxGrid::SetGridCursor() and wxGrid::ShowCellEditControl() for each row.

But I really have my doubts that will work in the end...
Use the source, Luke!
RichardW
Earned a small fee
Earned a small fee
Posts: 14
Joined: Fri Apr 25, 2014 4:45 am

Re: Custom combobox grid editor to default to selected items

Post by RichardW »

doublemax wrote:Maybe you could try wxGrid::SetGridCursor() and wxGrid::ShowCellEditControl() for each row.

But I really have my doubts that will work in the end...
I tried it and your doubts are well-founded--It did not work.

Would events not work for something like this? To activate the editor, I must first select the cell either by left-clicking or the arrow keys. Then I must activate it either by left-clicking or pressing the spacebar. This is programmatically translated to:

Code: Select all

wxGridEvent Ev(pogrid->GetId(), wxEVT_GRID_SELECT_CELL,pogrid,row,col);
pogrid->GetEventHandler()->ProcessEvent(Ev);
wxGridEvent Ev2(pogrid->GetId(), wxEVT_GRID_CELL_LEFT_CLICK,pogrid,row,col);
pogrid->GetEventHandler()->ProcessEvent(Ev2);
Unfortunately, this does not work. Do you think there's something along this line that *would* work?

Thanks for all your help so far, by the way, Doublemax.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Custom combobox grid editor to default to selected items

Post by doublemax »

I tried it and your doubts are well-founded--It did not work.
My doubts were more about the whole idea of displaying multiple cell editors at once.
Would events not work for something like this?
No, the events are sent in response to these actions, they are not the ones which initiate the action.

I'd try to trace through the ShowCellEditControl() calls and try to find out why it doesn't work as expected.

As a last resort, you could try to emulate the mouse clicks at low level (which of course would be very ugly):
http://docs.wxwidgets.org/trunk/classwx ... lator.html
Use the source, Luke!
RichardW
Earned a small fee
Earned a small fee
Posts: 14
Joined: Fri Apr 25, 2014 4:45 am

Re: Custom combobox grid editor to default to selected items

Post by RichardW »

OK, I'll take a look at this. Thanks for your help.

It should also be noted that the wxGridCellEnumEditor works like this as well.

Richard
RichardW
Earned a small fee
Earned a small fee
Posts: 14
Joined: Fri Apr 25, 2014 4:45 am

Re: Custom combobox grid editor to default to selected items

Post by RichardW »

Well, here's the update: I've copped out and worked around this. The whole reason I wanted to do this was to hide the meaningless ID with the meaningful text. So, instead of storing the ID in the base table, I'm storing the text, so that's what appears instead of the ID. This does basically what I want. When I need to store the ID, I'll refer to the clientdata in the combo rather than the base table.

This may cause problems in the future as the ID should always be unique but the text is not necessarily. It doesn't affect the table I'm currently querying (I believe there's a unique index on the text field) but I'm afraid I'll get bitten on the bum in the future.

Anyway, thanks for your help. I'll be back with another ridiculous question soon enough. :)

Richard
Post Reply