It was mentioned in some of my other threads on my app on that my code was incorrect because the apply button is present. I understand that now. It was said to collect the controls and then when apply is hit to send the data.
I have now idea how to approach that. So for the sake of general education. I have 1 property sheet and 5 property pages. For the sake of just general controls in use. Between all the 5, there are only radio controls and buttons, no edit controls (yet).
Let's assume there are 2 radios buttons and 1 button on each page.. where page 1 is radio1, radio2, button 1 and page 2 is radio3, radio4, button2....and so on.
I know that when the user selects something like a button or radio that the IsModified(TRUE) needs to be called to enable the apply button from grayed to active.
What would the code look like to scan all the controls and then apply them? I've never done it and I can't seem to find an example that isn't already super busy to gain the understanding of how to do it.
Anyone have a tutorial or code snippet or build a primer from the controls that I described above that could demonstrate how to execute this?
Update:
Ok so I have the DDX variables added:
void CSettingsUserTabs::DoDataExchange(CDataExchange* pDX)
{
CMFCPropertyPage::DoDataExchange(pDX);
DDX_Control(pDX, STYLE_3D_USER, m_style_3d);
DDX_Control(pDX, STYLE_FLAT_USER, m_style_flat);
DDX_Control(pDX, STYLE_FLAT_SHARED_HORZ_SCROLL_USER, m_style_flat_shared_h_scroll);
DDX_Control(pDX, STYLE_3D_SCROLLED_USER, m_style_3d_scroll);
DDX_Control(pDX, STYLE_3D_ONENOTE_USER, m_style_onenote);
DDX_Control(pDX, STYLE_3D_VS2005_USER, m_style_vs2005);
DDX_Control(pDX, STYLE_3D_ROUNDED_USER, m_style_3d_rounded);
DDX_Control(pDX, STYLE_3D_ROUNDED_SCROLL_USER, m_style_3d_rounded_scroll);
}
My radio selection look like:
void CSettingsUserTabs::OnBnClicked3dUser()
{
//AfxGetMainWnd()->SendMessage(WM_COMMAND, STYLE_3D_USER);
UpdateData(TRUE);
}
void CSettingsUserTabs::OnBnClickedFlatUser()
{
// TODO: Add your control notification handler code here
//AfxGetMainWnd()->SendMessage(WM_COMMAND, STYLE_FLAT_USER);
UpdateData(TRUE);
}
..... and the rest of them....
So to me, when I click any radio button, I expect that it scans all of controls on that property page? If so, all the variables have the values..do I call IsModified(); to enable the apply button...which then executes all the radio values i.e. only one selected? Is that the flow?
Update 2:
So this is what my modal dialog code is in MainFrame.cpp:
void CMainFrame::OnSettingsTools()
{
SettingsSheet SettingsSheet(L"Application Settings");
CSettingsPowerUser pgePowerUser;
CSettingsToolbars pgeToolbars;
CSettingsTheme pgeTheme;
CSettingsUserTabs pgeUserTabs;
CSettingsReset pgeReset;
SettingsSheet.AddPage(&pgeToolbars);
SettingsSheet.AddPage(&pgeTheme);
SettingsSheet.AddPage(&pgeUserTabs);
SettingsSheet.AddPage(&pgePowerUser);
SettingsSheet.AddPage(&pgeReset);
INT_PTR nRet = -1;
nRet = SettingsSheet.DoModal();
// Handle the return value from DoModal
switch (nRet)
{
case -1:
AfxMessageBox(_T("Dialog box could not be created!"));
break;
case IDABORT:
// Do something
AfxMessageBox(_T("ABORT!"));
break;
case IDOK:
// Do something
OnUserTabStyles(1);
AfxMessageBox(_T("OK!"));
break;
case IDCANCEL:
// Do something
AfxMessageBox(_T("CANCEL"));
break;
default:
// Do something
break;
};
}
The routine to verify if any of the radios buttons are changed in SettingsTabs.cpp:
void CSettingsUserTabs::OnTabRadioClicked(UINT nCmdID)
{
BOOL IsChecked = nCmdID;
CheckRadioButton(STYLE_3D_USER, STYLE_3D_ROUNDED_SCROLL_USER, nCmdID);
UpdateData(TRUE);
m_tabCmdID = nCmdID;
SetModified();
}
What the member variables look like in SettingsUserTabs.cpp:
void CSettingsUserTabs::DoDataExchange(CDataExchange* pDX)
{
CMFCPropertyPage::DoDataExchange(pDX);
DDX_Radio(pDX, STYLE_3D_USER, m_style_3d);
DDX_Radio(pDX, STYLE_FLAT_USER, m_style_flat);
DDX_Radio(pDX, STYLE_FLAT_SHARED_HORZ_SCROLL_USER, m_style_flat_h_scroll);
DDX_Radio(pDX, STYLE_3D_SCROLLED_USER, m_style_3d_scroll);
DDX_Radio(pDX, STYLE_3D_ONENOTE_USER, m_style_3d_onenote);
DDX_Radio(pDX, STYLE_3D_VS2005_USER, m_style_vs2005);
DDX_Radio(pDX, STYLE_3D_ROUNDED_USER, m_style_3d_rounded);
DDX_Radio(pDX, STYLE_3D_ROUNDED_SCROLL_USER, m_style_3d_rounded_scroll);
}
What the constructor looks like in SettingsUserTabs.cpp:
CSettingsUserTabs::CSettingsUserTabs()
: CMFCPropertyPage(IDD_SETTINGS_TABS)
, m_style_3d(FALSE)
, m_style_flat(FALSE)
, m_style_flat_h_scroll(FALSE)
, m_style_3d_scroll(FALSE)
, m_style_3d_onenote(FALSE)
, m_style_vs2005(FALSE)
, m_style_3d_rounded(FALSE)
, m_style_3d_rounded_scroll(FALSE)
, m_tabCmdID(FALSE)
{
}
This issue I'm seeing now is when I try to use the member variable m_tabCmdID it is coming back to unknown identifier so I'm not sure why the member variable isn't be seen. I am was expecting to use it like OnUserTabStyles(m_tabCmdID); so that it would pass the argument of the selected button to the method OnUserTabStyles. For now I just dumped a 1 in there to see if the mechanism works. I just am not clear how to access the member variable from the SettingsUserTabs.cpp from the IDOK. What am I missing?
EDIT: The range of options are sequential in the resource.h as 200-207, that is something I'm aware of and I know many don't like range options as they can get corrupted...this is my code, so I have no worries about the range being messed with.
Update 3:
Ok, so I finally understand the mechanism that Constantine described with the help of:
https://helgeklein.com/blog/2009/10/radio-buttons-in-mfc-visual-studio-2008-c/
I didn't have the tab order right nor did I have the first control set to true for the group.
With that, I now get the values 0-7 mentioned in the button group when I debug as I click each radio button based on its position in the group from 0-7 i.e. 8 buttons. Here is what the code looks like now.
SettingsUserTabs.cpp:
CSettingsUserTabs::CSettingsUserTabs()
: CMFCPropertyPage(IDD_SETTINGS_TABS)
, m_style_tabs(FALSE)
{
}
void CSettingsUserTabs::DoDataExchange(CDataExchange* pDX)
{
CMFCPropertyPage::DoDataExchange(pDX);
DDX_Radio(pDX, STYLE_3D_USER, m_style_tabs);
}
void CSettingsUserTabs::OnTabRadioClicked(UINT nCmdID)
{
UpdateData(TRUE);
BOOL RadioValueSelected = m_style_tabs; // only here to see 0-7 value for debugging only, not needed, test only
SetModified();
}
The mainframe.cpp above (Update 2): void CMainFrame::OnSettingsTools() is still the same.
So now here is where my question still isn't clear, I call the domodal from mainframe.cpp, the member variable m_style_tabs is in SettingsUserTabs.cpp. When I try to access the member variable, it says unknown identifier when I try to do something like this after the domodal int temp = m_styles_tabs;. I have a this in the mainframe.cpp
void CMainFrame::DoDataExchange(CDataExchange* pDX)
{
// TODO: Add your specialized code here and/or call the base class
CMDIFrameWndEx::DoDataExchange(pDX);
}
I would expect that the member would be seen in mainframe.cpp so I can process it after the domodal which is what I thought the whole point of this is?
How do I access the member variable so I can do the OnApply to it? I think I almost got it, I am just unclear on how to execute the last few steps for the actual apply itself.
Update 4:
The reason I ask about the restart is when the user selects the tabs property page and choose 1 of 8 choices, that option is stored in the registry and read during the Oncreate and then uses that style. Since it is OnCreate I haven't found a way to "redraw" or use the new tab setting except to restart the app. So by doing the reg save > respawn > end old ...I would want the dialog to reopen at the same tab property page so the user can see what the changes are if the apply was selected...vs. some dialog that says "restart"..or whatever. Below is the code used to demonstrate how it is working now.
OutputWnd.cpp
int COutputWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDockablePane::OnCreate(lpCreateStruct) == -1)
return -1;
CRect rectDummy;
rectDummy.SetRectEmpty();
// Create User Define tab style:
int UserTabStyle = AfxGetApp()->GetProfileInt(_T("Settings"), _T("UserTabStyle"), 0); //Get value from registry
// If the key doesn't exist, UserTableStyle will be 0 or FALSE;
if (UserTabStyle != FALSE && UserTabStyle <= 8) { // User selected tab style type
int EnumUserTabStyle = UserTabStyle - 1; // Fix enum if key doesn't exist.
if (!m_wndTabs.Create(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle), rectDummy, this, 1))
{
TRACE0("Failed to create output tab window\n");
return -1; // fail to create
}
}
else { // Default tabs style if Reg key does not exist i.e. new install/program reset
if (!m_wndTabs.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, 1))
{
TRACE0("Failed to create output tab window\n");
return -1; // fail to create
}
}
… rest of function....
Update 5:
Here is the Apply in use from SettingsUserTabs.cpp:
BOOL CSettingsUserTabs::OnApply()
{
// TODO: Add your specialized code here and/or call the base class
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
return CMFCPropertyPage::OnApply();
}
Update 6:
Everything to this point is working, I ran into a road block trying to apply lessons learned here and at this link:
MFC MDI Substituting a class member dynamically
What was done in this topic was for the OutputWnd pane which works brilliantly! My MDI opens a file and uses CTabView and using the link shown in Update 6 allowed me to change the tabs on boot. Now that I have the OutputWnd doing it with OnApply, I'm trying to apply it to the document view when a file is loaded. I'm running into an access violation when I call the new function I created in TrainView.cpp and calling it from UserSettingsTabs.cpp. I thought it was the static_cast operation, but even if I do a simple bold using GetControlTabs() that also crashes (Shown in the commented out code, was on boot, now in OnApply to test theory). So clearly I need to capture the MDI document but not sure how that is done. I thought it would be as simple as:
GetTabControl().ModifyTabStyle(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle));
But when that crashed with a Cx000000005 access violation, I knew something was wrong on my end. I can't modify the CTabView operation, so I'm looking to see if we can fix what I'm doing wrong to have the OnApply change the tab styles without restarting as done in the OutputWnd we just fixed.
So as it stands, the OnApply that is working and now modded to try an integrate the CTabView functionality:
SettingsUserTabs.cpp:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
CTrainView* TrainTabs; // User Call from anywhere method
TrainTabs->TrainDocUpdateTabsControl();
}
return bResult;
}
I added the function CTrainView::TrainDocUpdateTabsControl() to update the tabs...the rest of the code is fully operation i.e. void CTrainView::OnInitialUpdate()
The TrainView.cpp:
IMPLEMENT_DYNCREATE(CTrainView, CTabView)
void CTrainView::OnInitialUpdate()
{
CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
pMainFrame->m_wndOutput.AddStringDebugTab(_T("Debug: TrainView--CTrainView::OnInitialUpdate()."));
// add views // cmb
AddView(RUNTIME_CLASS(CInformationView), AfxStringID(IDS_INFORMATION));
AddView(RUNTIME_CLASS(CChaptersView), AfxStringID(IDS_CHAPTERS));
// Nicely hack to access protected member
class CMFCTabCtrlEx : public CMFCTabCtrl
{
public:
void SetDisableScroll() { m_bScroll = FALSE; }
};
// One-Liner to Disable navigation control
((CMFCTabCtrlEx*)&GetTabControl())->SetDisableScroll();
GetTabControl().EnableTabSwap(TRUE);
GetTabControl().SetLocation(CMFCBaseTabCtrl::Location::LOCATION_BOTTOM);
//GetTabControl().SetActiveTabBoldFont(TRUE);
GetTabControl().EnableAutoColor(TRUE);
// Modify User Define tab style:
int UserTabStyle = AfxGetApp()->GetProfileInt(_T("Settings"), _T("UserTabStyle"), 0); //Get value from registry
// If the key doesn't exist, UserTableStyle will be 0 or FALSE;
if (UserTabStyle != FALSE && UserTabStyle <= 8) { // User selected tab style type
int EnumUserTabStyle = UserTabStyle - 1; // Fix enum if key doesn't exist.
GetTabControl().ModifyTabStyle(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle));
}
else { // Default tabs style if Reg key does not exist i.e. new install/program reset
GetTabControl().ModifyTabStyle(CMFCTabCtrl::STYLE_FLAT);
}
CTabView::OnInitialUpdate();
}
void CTrainView::TrainDocUpdateTabsControl()
{
CTabView::AssertValid();
GetTabControl().SetActiveTabBoldFont(TRUE); << CAUSES Cx000000005 ACCESS ERROR CRASH WHEN CALLED.
//int EnumUserTabStyle;
//int UserTabStyle = AfxGetApp()->GetProfileInt(_T("Settings"), _T("UserTabStyle"), 0); //Get value from registry
//((CMainFrame*)AfxGetMainWnd())->GetTabControl().ModifyTabStyle(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle));
}
Update 6 EDIT:
The tabview is created from Application.cpp like this:
//Load Train Template
m_pkDocTrainTemplate = new CMultiDocTemplate(
IDR_TRAIN, // Loads TRAIN operation
RUNTIME_CLASS(CTrainDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CTrainView));
AddDocTemplate(m_pkDocTrainTemplate);
I tried to cast the m_pkDocTrainTemplate as I thought that was the pointer? Since it is MDI, I wasn't sure if there was an index issue since there can be multiple docs open simultaneously?
Below is an example of an application I wrote some time ago. It's a simple "Settings" dialog. Unlike yours, this one is derived from CDialogEx. But as CPropertyDialog is derived from CDialog, these apply in your case too.
Using the Wizard, I added member variables to the dialog class, bound to the dialog controls. Choose "Value", rather than "Control" in the "Category" combo in the Wizard. These are declared in the class definition. For simplicity, I only show three. There is a CString, an int and a BOOL variable, bound to an edit, a combo-box (drop-down list) and a check-box control respectively.
class CSettingsDlg : public CDialogEx
{
.
.
public:
CString m_DBConn;
int m_DumpSQL;
BOOL m_bLineNums;
}
In the implementation, the Wizard has modified the constructor and the DoDataExchange() member functions:
CSettingsDlg::CSettingsDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CSettingsDlg::IDD, pParent)
, m_DBConn(_T(""))
, m_DumpSQL(0)
, m_bLineNums(FALSE)
{
}
void CSettingsDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT_DBCONN, m_DBConn);
DDV_MaxChars(pDX, m_DBConn, 255);
DDX_CBIndex(pDX, IDC_COMBO_DUMPSQL, m_DumpSQL);
DDV_MinMaxInt(pDX, m_DumpSQL, 0, 2);
DDX_Check(pDX, IDC_CHECK_LINENUMS, m_bLineNums);
}
The values in the constructor are the initial (default) ones. The DoDataExchange() function calls the DDX/DDV routines. The DDX routines perform the transfer of data (controls<->variables), while the DDV ones the validation - they are optional. The DoDataExchange() function is called by UpdateData(). Also, the default implementation of OnOK() calls UpdateData(TRUE), and if successful closes the dialog.
You will need to enable the Apply button if something has been modified. You can capture notification messages like EN_CHANGE, EN_UPDATE, BN_CLICKED etc (add an event in the property editor) and call the SetModified() function - this can prove quite tedious, but I can't see any other way to do it.
Such a dialog class can be used in the application as shown below:
void CChildView::OnSetoptions()
{
// Create a Settings-dialog class instance
CSettingsDlg sd; // Main application window as parent - will block every UI item in the application
// Set initial values for the member variables
sd.m_DBConn = szDBconn;
sd.m_DumpSQL = nDumpSQL;
sd.m_bLineNums = bDumpLineNums;
if (sd.DoModal() == IDOK)
{
// Store the values entered by the user
lstrcpyn(szDBconn, sd.m_DBConn, MAX_PATH);
nDumpSQL = sd.m_DumpSQL;
bDumpLineNums = sd.m_bLineNums;
}
}
EDIT :
I have a usage example in the code section just above. The procedure is create an instance of the dialog class, set the member variables' values (initial values, eg read from the registry), call DoModal() and if successful store (copy) the variables somewhere else (if not discard them). This should be done in some event handler, like CMainFrame::OnSettingsTools() in Update2. The CMainFrame::DoDataExchange() override makes no sense.
It's quite strange that you can't access the m_style_tabs variable. Isn't it a non-static, public member of the CSettingsUserTabs class? It should be declared in SettingsUserTabs.h. Won't it work if you acceess it as pgeUserTabs.m_style_tabs? In the CSettingsUserTabs class functions it can accessed simply as m_style_tabs. Also I see it is initialized as FALSE in the constructor. Is it a BOOL and not an int? (btw BOOL is defined as int in Win32, so the compiler won't complain) But the Wizard generates an int variable for radio-buttons, optionally with range validation as well.
Another point, you don't normally need to call UpdateData(TRUE) in CSettingsUserTabs::OnTabRadioClicked(). Please leave only the SetModified() call there. UpdateData(TRUE) is typically called in the OnOK() function. And usually you don't need to override these, because the default implementation is sufficient. The CPropertyPage documentation btw mentions that The default implementation of OnApply calls OnOK.
EDIT 2 :
In OnApply() you should first check if validation was successful. Also, OnCreate() isn't a "method" that can be called directly. It should be considered an "event". It's called by the framework when a window is created. You should instead call Create(). In your case you can destroy the Output Window and create it anew (with the new style). But, I see that the CMFCTabCtrl class has a ModifyTabStyle() function, which you can try calling, (without destroying the windows and creating it again). So, your code would become:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFraime*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
}
return bResult;
}
The above code won't compile, because the m_wndOutput and m_wndTabs members are protected. You will have to make them public.
I am working with a MFC application, and would like to remove the dynamic entry that appears under "Arrange Icons".
I am not sure how this is added to the application, or how could I switch that off?
The list of open windows is added to the "Window" menu by the MFC framework, in the application's main window handler for the WM_INITMENUPOPUP command. (Actually, the framework adds the items to all menus that already contain any command with an ID between AFX_IDM_WINDOW_FIRST and AFX_IDM_WINDOW_LAST – which includes the "Cascasde," "Tile..." and "Arrange Icons" default values.)
You can remove these items by adding the ON_WM_INITMENUPOPUP() handler to your frame window's message map and overriding that frame's OnInitMenuPopup() member function.
Assuming your main frame is derived from the CMDIFrameWnd class, this override would look something like the following:
void MyFrameWnd::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);// Call base class FIRST
// <Insert any other code for your override>
UINT commID = AFX_IDM_FIRST_MDICHILD; // MFC gives the first item this ID
BOOL hadID;
do {
hadID = pPopupMenu->RemoveMenu(commID, MF_BYCOMMAND);
++commID;
} while (hadID);
return;
}
The declaration of the function in your class should properly have the afx_msg attribute, as shown below, although this is generally defined as 'nothing' in the recent MFC versions:
class MyFrameWnd : public CMDIFrameWnd
{
//...
protected:
afx_msg void OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu);
//...
You could also reduce the removal 'loop' code to the following single line, if you prefer such compressed code:
for (UINT commID = AFX_IDM_FIRST_MDICHILD; Menu->RemoveMenu(commID, MF_BYCOMMAND); ++commID) ; // Empty loop
Note that this removal process will leave a 'separator' at the bottom of your application's "Window" menu; removing this as well would involve a bit more work, as that separator doesn't have a command ID – so you would need to determine its position (index) in the menu and call the RemoveMenu() function using that index and the MF_BYPOSITION flag as the second argument.
Using the code from the first snippet, and assuming that separator is at the very end of your menu, this could be achieved by adding the following code after the removal loop:
if (commID > AFX_IDM_FIRST_MDICHILD + 1) { // We removed at least one item, so ...
int nItems = Menu->GetMenuItemCount(); // ... remove the separator,
pPopupMenu->RemoveMenu(nItems - 1, MF_BYPOSITION); // assuming it's the last item!
}
I would like to show a label and execute a function after displaying the label. Unfortunately, the label is always displayed after the function is executed.
void MainWindow::showLabel(){
myLabel->show();
doSomething();
}
void MainWindow::doSomething(){
QThread::msleep(3000);
myLabel->hide();
}
So, when i execute my code, the programm waits for three seconds and does show me an empty window afterwards (since it directly hides the label before even showing it; if I comment the hide function, the label is shown after waiting three seconds).
What I've tried to do is modifying the showEvent like this:
void MainWindow::showEvent(QShowEvent *event) {
QMainWindow::showEvent(event);
doSomething();
}
Am I doing something wrong by modifying the method or is there any other way to show the label before executing the followed function?
I would solve your problem in the following way:
void MainWindow::showLabel()
{
myLabel->show();
// Wait for 3sec. and hide the label.
QTimer::singleShot(3000, myLabel, SLOT(hide()));;
}
i.e. you don't need the second function and to block the current thread with QThread::msleep(), which is the reason why your label appears after the timeout is fired.
Update
If you need to do more than just hiding a label, define a slot and call it like:
void MainWindow::showLabel()
{
myLabel->show();
// Wait for 3sec. and call a slot.
QTimer::singleShot(3000, this, SLOT(doSomething()));
}
// This is a slot
void MainWindow::doSomething()
{
myLabel->hide();
[..]
// some more stuff
}
QThread::msleep(3000); is blocking the main thread where event loop is processed. So it prevent to show myLabel until sleep time is end. The solution is either to use QTimer as vahancho recomended or call event loop processing manualy by calling QEventLoop::exec() after myLabel->show();.
I'm creating an application using gtkmm, where I create a number of Gtk::Boxes then display them as need be to the screen. These boxes display Gtk::Entry widgets to get information from the user, along with other widgets.
My problem is that when I try and "remove" these boxes, they don't seem to actually get removed, so they take up memory which results in my application crashing. I'm unsure of why it's doing this, but what I find interesting is this: every time my "remove_widget" function (which removes the Gtk::Box I want removed) executes, I have a std::cout statement which displays "Container Removed". Each time, it should theoretically only remove the container element (the Gtk::Box). But each subsequent time it runs, the number of "Container Removed" statements doubles. So the first time it gets displayed once, the next time it gets displayed twice, then four times, then eight, etcetera.
Anyways, the core of what I want to do is add a specific box to the window, get information from it, then destroy it should I can add a different Gtk::Box. I don't know if this method is the best way to do this, but as of right now it's how I'm going about it. Anyways, here is the code to get you an idea. What's happening is that I load MainWin, which creates a new window. Then MainWin packs an Event_Controller box into it, and the Event_Controller handles displaying the other boxes I want to display for the user. on_new_flight_clicked() handles the displaying the first box, and on_next_clicked handles the displaying the rest of the boxes.
class MainWin : public Gtk::Window {
public:
MainWin();
virtual ~MainWin();
protected:
// signal handler
void on_button_clicked(Glib::ustring data);
// Flight Data Object
Flight flight;
// Member widgets:
Gtk::Box main_container_box; // Holds everything in the window
Gtk::Button new_comp; // Allows the user to create a new competition
Gtk::Button go_home; // Allows the user to return to the main
screen
Event_Controller* new_event;
};
class Event_Controller : public Gtk::Box {
public:
Event_Controller(Flight& tflighto);
virtual ~Event_Controller();
protected:
Flight* flight;
Gtk::Box* container_box;
void on_new_flight_clicked();
void on_next_clicked(Glib::ustring data);
void remove_widget();
Gtk::Button next; // This will appear at every step of the create a
flight sequence, except the end, taking the user to the next step
};
void Event_Controller::on_new_flight_clicked()
{
container_box = new Gtk::Box; // Create a new container box
add(*container_box); // Add it to the box
container_box->pack_start(*Gtk::manage(new Flight_Letter(flighto)),
Gtk::PACK_EXPAND_WIDGET); // Pack the first form into the container
box
add(next); // Pack the next button outside the container box
show_all_children(); // Show everything
}
void Event_Controller::remove_widget()
{
// Remove everything from Event_Controller, this is where I think* things go wrong
remove(*container_box);
delete container_box;
container_box = nullptr;
cout << "Container Removed..." << endl;
remove(next);
container_box = new Gtk::Box; // Create the container box
add(*container_box);
}
void Event_Controller::on_next_clicked(Glib::ustring data)
{
int figures_loop = 0;
// This will show the Flight_Number form. Here is the general model
for what we do in this function. This function
// gets passed an argument, and that allows us to remove the form we
need to and bring up a new form.
if(data == "first")
{
// First we remove the previous form and the container
remove_widget();
// Then we set next to send a message to this function when it's
clicked, so that it knows what form to bring up next
next.signal_clicked().connect(sigc::bind<Glib::ustring>
(sigc::mem_fun(*this, &Event_Controller::on_next_clicked), "first"));
// Create the new form
container_box->pack_start(*Gtk::manage(new
Figure_Number(num_of_figures)), Gtk::PACK_EXPAND_WIDGET);
// Pack the next button
pack_start(next);
// Show all the widgets
show_all_children();
}
// There is more to this function, I just left it out for brevity sake
}
Thanks for any help ahead of time, and if anything is unclear or you have any suggestion/concerns/comments, feel free to comment further.
I have a .NET form with a boolean property called _isResized, which is set to true when the SizeChanged event is called and is set to false when the bool isResized() function is called.
ref class net_window : public System::Windows::Forms::Form
{
private:
bool _isResized;
public:
net_window(void)
: _isResized(false){
InitializeComponent();
}
void InitializeComponent(void){
this->SizeChanged += gcnew EventHandler(this, &net_window::eventResized);
}
bool isResized(void){
bool temp = _isResized;
_isResized = false;
return temp;
}
Void eventResized(Object^ sender, EventArgs^ e){
_isResized = true;
}
};
I also have an native C++ class called window which acts as a layer around the .NET form. This class is updated every frame of the main loop and uses the bool isResized() function of the form to see if the size has changed.
class Window
{
private:
void* _net_window_handle;
Window(){
_net_window_handle = new gcroot<net_window^>;
(*(gcroot<net_window^>*)_net_window_handle) = gcnew net_window;
}
~Window(){
delete _net_window_handle;
}
void update(void)//Called every frame.
{
if( (*(gcroot<net_window^>*)_element)->isResized() )
//Do stuff.
}
};
The delegate added to SizeChanged is called whenever the form is being resized, so _isResized is set to true as soon as the form size changes, but for some reason the thread of the main loop freezes whenever the window class uses the bool isResized() function, until the user has released the edge of the form and thus the resizing has finished.
Is it not possible to access .NET form values as long as it is being resized. Is there an alternative?
Thanks
EDIT
I use a main loop which calls System::Windows::Forms::Application::DoEvents() every frame. When the thread of the loop enters this function and the main window is resizing, it freezes. Is there a way to avoid this problem.
DoEvents strikes again. Windows pumps a modal message loop when the user starts dragging a window edge. Which means that your DoEvents loop isn't running anymore. Use a timer instead, SetTimer with a 15 or 31 msec interval is about right. Having your code respond to the Resize event directly of course highly preferred.
Thanks Hans Passant