wxTextValidator вызывает сбой - PullRequest
0 голосов
/ 05 июня 2018

У меня есть диалоговое окно с некоторыми динамическими элементами управления.По сути, при создании я передаю указатель на структуру, внутри этой структуры находится контейнер std::pair<wxString,wxString>.Для каждого из них мне нужно создать еще одну строку из трех элементов управления: wxTextCtrl, wxChoice и wxButton.

. Эта часть работает на основе доступных элементов в контейнере.успешно создать их при построении диалога.Я также использую wxTextValidator для соединения wxTextCtrl с первым wxString в паре.Так что, если в контейнере три пары, у меня будет три wxTextCtrl, и у каждой есть валидатор, соответствующий wxString.

После wxID_OK диалога, текстовые валидаторы работаюти моя исходная структура, которую я передал ей в качестве указателя, содержит правильные данные.

Однако я также добавил в свой диалог кнопку, которая должна позволять создавать новые записи std::pair<wxString,wxString> вконтейнер.Если я это сделаю, при wxID_OK приложение вылетает.DrMingW создает впечатление, что указатель на wxString недействителен, но это невозможно, поскольку я создаю новую запись в своем контейнере при нажатии кнопки и указываю wxTextValidator на эту вновь созданную запись,Он сохраняется вне диалогового окна, поэтому он не становится недействительным при закрытии диалогового окна.

Моя структура данных:

struct StoryNodeData
{
    wxString title;
    wxString text;
    std::vector<std::pair<wxString,wxString>> options;
};

Заголовок моего диалогового окна:

#ifndef STORYDIALOG_H
#define STORYDIALOG_H

#include "flowchart.hpp"

#include <map>

//(*Headers(StoryDialog)
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/choice.h>
#include <wx/dialog.h>
#include <wx/gbsizer.h>
#include <wx/scrolwin.h>
#include <wx/sizer.h>
#include <wx/textctrl.h>
//*)

class StoryDialog: public wxDialog
{
    public:

        StoryDialog(wxWindow* parent,StoryNodeData* data, wxArrayString titles,wxWindowID id=wxID_ANY,const wxPoint& pos=wxDefaultPosition,const wxSize& size=wxDefaultSize);
        virtual ~StoryDialog();

        void HandleOptionDelete(wxCommandEvent& e);

        //(*Declarations(StoryDialog)
        wxButton* OptionButton;
        wxFlexGridSizer* OptionsSizer;
        wxScrolledWindow* OptionsWindow;
        wxTextCtrl* EventTextCtrl;
        wxTextCtrl* TitleTextCtrl;
        //*)

    protected:

        //(*Identifiers(StoryDialog)
        static const long ID_TEXTCTRL1;
        static const long ID_TEXTCTRL2;
        static const long ID_BUTTON1;
        static const long ID_TEXTCTRL3;
        static const long ID_CHOICE1;
        static const long ID_BITMAPBUTTON1;
        static const long ID_SCROLLEDWINDOW1;
        //*)

    private:

        //(*Handlers(StoryDialog)
        void OnNewOptionButtonClick(wxCommandEvent& event);
        void OnBitmapButton1Click(wxCommandEvent& event);
        //*)

        std::map<wxObject*,std::tuple<wxTextCtrl*, wxChoice*, std::size_t>> m_optionCtrls;
        StoryNodeData* m_data;
        wxArrayString m_titles;

        void ConstructOption(wxString* text, wxString* title);

        DECLARE_EVENT_TABLE()
};

#endif

Иреализация диалога:

#include "StoryDialog.h"

#include <wx/artprov.h>
#include <wx/valgen.h>

//(*InternalHeaders(StoryDialog)

#include <wx/artprov.h>

#include <wx/bitmap.h>

#include <wx/image.h>

#include <wx/intl.h>

#include <wx/string.h>

//*)

//(*IdInit(StoryDialog)

const long StoryDialog::ID_TEXTCTRL1 = wxNewId();

const long StoryDialog::ID_TEXTCTRL2 = wxNewId();

const long StoryDialog::ID_BUTTON1 = wxNewId();

const long StoryDialog::ID_TEXTCTRL3 = wxNewId();

const long StoryDialog::ID_CHOICE1 = wxNewId();

const long StoryDialog::ID_BITMAPBUTTON1 = wxNewId();

const long StoryDialog::ID_SCROLLEDWINDOW1 = wxNewId();

//*)

BEGIN_EVENT_TABLE(StoryDialog,wxDialog)
    //(*EventTable(StoryDialog)
    //*)
END_EVENT_TABLE()

StoryDialog::StoryDialog(wxWindow* parent,StoryNodeData* data, wxArrayString titles,wxWindowID id,const wxPoint& pos,const wxSize& size)
{
    //(*Initialize(StoryDialog)

    wxBitmapButton* BitmapButton1;

    wxChoice* Choice1;

    wxFlexGridSizer* FlexGridSizer1;

    wxGridBagSizer* GridBagSizer1;

    wxStaticBoxSizer* StaticBoxSizer1;

    wxStdDialogButtonSizer* StdDialogButtonSizer1;

    wxTextCtrl* TextCtrl1;


    Create(parent, wxID_ANY, _("New Event"), wxDefaultPosition, wxDefaultSize, wxSTAY_ON_TOP|wxCAPTION|wxRESIZE_BORDER|wxCLOSE_BOX|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxSIMPLE_BORDER, _T("wxID_ANY"));

    GridBagSizer1 = new wxGridBagSizer(0, 0);

    GridBagSizer1->AddGrowableCol(0);

    GridBagSizer1->AddGrowableRow(1);

    TitleTextCtrl = new wxTextCtrl(this, ID_TEXTCTRL1, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_NONE,&data->title), _T("ID_TEXTCTRL1"));

    GridBagSizer1->Add(TitleTextCtrl, wxGBPosition(0, 0), wxDefaultSpan, wxALL|wxEXPAND, 5);

    EventTextCtrl = new wxTextCtrl(this, ID_TEXTCTRL2, wxEmptyString, wxDefaultPosition, wxSize(259,138), wxTE_MULTILINE|wxVSCROLL, wxTextValidator(wxFILTER_NONE,&data->text), _T("ID_TEXTCTRL2"));

    GridBagSizer1->Add(EventTextCtrl, wxGBPosition(1, 0), wxDefaultSpan, wxALL|wxEXPAND, 5);

    StdDialogButtonSizer1 = new wxStdDialogButtonSizer();

    StdDialogButtonSizer1->AddButton(new wxButton(this, wxID_OK, wxEmptyString));

    StdDialogButtonSizer1->AddButton(new wxButton(this, wxID_CANCEL, wxEmptyString));

    StdDialogButtonSizer1->Realize();

    GridBagSizer1->Add(StdDialogButtonSizer1, wxGBPosition(2, 0), wxGBSpan(1, 2), wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5);

    StaticBoxSizer1 = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Options"));

    FlexGridSizer1 = new wxFlexGridSizer(2, 1, 0, 0);

    FlexGridSizer1->AddGrowableCol(0);

    FlexGridSizer1->AddGrowableRow(1);

    OptionButton = new wxButton(this, ID_BUTTON1, _("New Option"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));

    FlexGridSizer1->Add(OptionButton, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);

    OptionsWindow = new wxScrolledWindow(this, ID_SCROLLEDWINDOW1, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER|wxVSCROLL, _T("ID_SCROLLEDWINDOW1"));

    OptionsSizer = new wxFlexGridSizer(0, 3, 0, 0);

    OptionsSizer->AddGrowableCol(0);

    TextCtrl1 = new wxTextCtrl(OptionsWindow, ID_TEXTCTRL3, _("Text"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_TEXTCTRL3"));

    OptionsSizer->Add(TextCtrl1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);

    Choice1 = new wxChoice(OptionsWindow, ID_CHOICE1, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE1"));

    OptionsSizer->Add(Choice1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);

    BitmapButton1 = new wxBitmapButton(OptionsWindow, ID_BITMAPBUTTON1, wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_DELETE")),wxART_BUTTON), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, _T("ID_BITMAPBUTTON1"));

    OptionsSizer->Add(BitmapButton1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);

    OptionsWindow->SetSizer(OptionsSizer);

    OptionsSizer->Fit(OptionsWindow);

    OptionsSizer->SetSizeHints(OptionsWindow);

    FlexGridSizer1->Add(OptionsWindow, 1, wxALL|wxEXPAND, 5);

    StaticBoxSizer1->Add(FlexGridSizer1, 1, wxEXPAND, 5);

    GridBagSizer1->Add(StaticBoxSizer1, wxGBPosition(0, 1), wxGBSpan(2, 1), wxALL|wxEXPAND, 5);

    SetSizer(GridBagSizer1);

    GridBagSizer1->Fit(this);

    GridBagSizer1->SetSizeHints(this);


    Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&StoryDialog::OnNewOptionButtonClick);

    //*)


    OptionsWindow->SetScrollRate(5,5);

    OptionsWindow->DestroyChildren();

    Bind(wxEVT_BUTTON, &StoryDialog::HandleOptionDelete, this);

    m_data = data;
    m_titles = titles;

    // construct the existing options
    for(auto itr = m_data->options.begin(); itr != m_data->options.end(); ++itr)
    {
        auto title = &((*itr).first);
        auto text = &((*itr).second);

        ConstructOption(text,title);
    }
}

StoryDialog::~StoryDialog()
{
    //(*Destroy(StoryDialog)
    //*)
}


void StoryDialog::HandleOptionDelete(wxCommandEvent& e)
{
    auto b = e.GetEventObject();
    auto itr = m_optionCtrls.find(b);
    if(m_optionCtrls.end() != itr)
    {
        auto button = itr->first;
        auto pa = itr->second;
        m_optionCtrls.erase(itr);

        OptionsSizer->Detach(std::get<0>(pa));
        OptionsSizer->Detach(std::get<1>(pa));
        OptionsSizer->Detach(reinterpret_cast<wxBitmapButton*>(button));

        std::get<0>(pa)->Destroy();
        std::get<1>(pa)->Destroy();
        reinterpret_cast<wxBitmapButton*>(button)->Destroy();

        auto index = std::get<2>(pa);
        auto itr = m_data->options.begin() + index;
        m_data->options.erase(itr);
    }

    OptionsSizer->Layout();
    OptionsWindow->SetSizer(OptionsSizer);
    SendSizeEvent();

    e.Skip(true);
}

void StoryDialog::OnNewOptionButtonClick(wxCommandEvent& event)
{
    m_data->options.push_back(std::make_pair("",""));

    auto itr = m_data->options.end()-1;

    auto text = &((*(itr)).second);
    auto title = &((*(itr)).first);

    ConstructOption(text,title);
}

void StoryDialog::ConstructOption(wxString* text, wxString* title)
{
    long tId = wxNewId();
    long cId = wxNewId();
    long bId = wxNewId();

    auto optionTextCtrl = new wxTextCtrl(OptionsWindow, tId, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_NONE,text));

    OptionsSizer->Add(optionTextCtrl, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);

    auto optionTitleCtrl = new wxChoice(OptionsWindow, cId, wxDefaultPosition, wxDefaultSize, m_titles, 0, wxGenericValidator(title));

    OptionsSizer->Add(optionTitleCtrl, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);

    auto deleteButtonCtrl = new wxBitmapButton(OptionsWindow, bId, wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_DELETE")),wxART_BUTTON), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator);

    OptionsSizer->Add(deleteButtonCtrl, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);

    OptionsSizer->Layout();
    OptionsWindow->SetSizer(OptionsSizer);
    SendSizeEvent();

    m_optionCtrls[deleteButtonCtrl] = std::make_tuple(optionTextCtrl,optionTitleCtrl,m_optionCtrls.size()-1);
}

Вы заметите, что способ создания динамических элементов управления одинаков, независимо от того, основан ли он на предоставленных данных, или на добавлении пользователем новых данных через элементы управления.Разница лишь в том, что при добавлении новых я сначала создаю записи в контейнере

1 Ответ

0 голосов
/ 05 июня 2018

Указатели на элементы std::vector<> могут (и будут) считаться недействительными при изменении содержимого вектора, например, когда вы добавляете к нему больше элементов, поэтому вы не можете сделать это следующим образом.

Вместо этого вынужно использовать контейнер, который не будет перемещать свои значения (например, std::[unordered_]map<>) или хранить unique_ptr<> s в вашем векторе.

...