[RESOLVED] CDialog:ListBox in Doc/View
Instantiating a dialog in a Doc function, I can update an Edit box in the dialog, but when I try to use AddString on a Listbox (using "simple text"), I get an Assertion Failure.
Using VC++6; to illustrate the problem in the simplest app, I created an SDI Doc/View app, added a Dialog class, added a new menu item to invoke the dialog, then added an Editbox (with member variable)and a CListbox (with control member variable) to the dialog. The fact that I can write to the Editbox suggests that the invocation of the Dialog is ok, but the Assertion Error for the List box is a puzzle. I can add data using AddString in an InitDialog function, but this is useless, since I want to add data to the ListBox at runtime. Must be a simple omission somewhere........
Re: CDialog:ListBox in Doc/View
More than likely you're calling AddString before the control has been created or after it's been destroyed. What is the ASSERT code telling you?
Re: CDialog:ListBox in Doc/View
Debug assertion failure message shows:
File: afxwin2.inl
Line: 669
In the .inl file at Line 668/669 is:
[668] _AFXWIN_INLINE int CListBox::AddString(LPCTSTR lpszItem)
[669] { ASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_ADDSTRING, 0, (LPARAM)lpszItem); }
As I wrote earlier, a statement which writes to an Edit Box on the Dialog form is successful, whereas a trying to AddString to the List Box at the same point in the function which instantiates the Dialog causes the Assertion failure. Looks to me like the CListBox::AddString command is expecting to find the Dialog in a standard place, rather than actually getting it from the instantiated Dlg. (Excuse my poor jargonese).
Re: CDialog:ListBox in Doc/View
May be you are missing DDX entry for the control or string. Please check if the variable name is present in your MESSAGE_MAP macro. Also, see to it that OnInitialUpdate() function calls the base class version in your view class.
Regards,
Bhushan
Re: CDialog:ListBox in Doc/View
That supports what I said in post #2. The Listbox window doesn't exist at the point you're trying to add strings to it.
Re: CDialog:ListBox in Doc/View
Thanks for reply, bhushan.
The Dialog class has a DDX entry for the ListBox (c_list):
void CDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDlg)
DDX_Control(pDX, IDC_LIST1, c_list);
DDX_Text(pDX, IDC_EDIT1, m_edit1);
//}}AFX_DATA_MAP
}
The MESSAGE_MAP for the entire Dialog class is empty. I would never guess in a million years what to put there, nor why.
I also cannot guess what to put in View::OnInitialUpdate.
This c**p drives me mad.
Re: CDialog:ListBox in Doc/View
I should add that I want to access the ListBox from the Doc: that's where the data is.
I'll also repeat the point: I can write to the EditBox in the dialog from a Doc function, but attempting to access the CListBox of the Dialog causes an assertion error. It is clear that the Doc class doesn't have a clue about the CListBox, but how to get round this? Or any suggestions as to how to present the user with a list of runtime-generated items, from which they can select any or all? Where in any MS info is there any reference to the need to hand crank a CListBox into life (other than when it's embedded in a CDialog-based appplication)? Maybe I need to use the dreaded GetDlgItem(..) ?
Re: CDialog:ListBox in Doc/View
Quote:
Originally Posted by
crasher
I should add that I want to access the ListBox from the Doc: that's where the data is.
I'll also repeat the point: I can write to the EditBox in the dialog from a Doc function, but attempting to access the CListBox of the Dialog causes an assertion error. It is clear that the Doc class doesn't have a clue about the CListBox, but how to get round this?
Your problem is in your design. You shouldn't access dialog controls from your doc, you should access data from your dialog. You can either pass the data from the doc to the dialog before you create it, or you can give the dialog a pointer to your doc and let the dialog ask for the information.
Quote:
Maybe I need to use the dreaded GetDlgItem(..) ?
What's dreaded about GetDlgItem?
Re: CDialog:ListBox in Doc/View
D_Drmmr:
You seem not to have read the posts.
This is a doc/view app. The app reads in a few Mb from various data files (1 to many), and converts and displays the data. The user must be given the choice of which of the various data sets to display together. Therefore a dialog needs to be invoked, in order to present a listbox identifying the data sets which are in memory. All the data is at that point in the Doc. The ListBox has to present the choices, but obviously has to access the identities of the data sets. So I need to pass some info from the Doc to the Dialog. The data is not pre-existing in the dialog, and this is not a dialog app.
You wrote:
"You can ... pass the data from the doc to the dialog before you create it".
That is what I am trying to do, and that is the problem!
I can indeed write to an EditBox in the dialog which I instantiate in a Doc function, but when I try to get the Listbox to accept data (AddString(..)) in the same function, a Windows Assertion Failure is generated. Therefore the ClistBox appears not to have been initialised or its pointer is bent. So there is something tricky about a CListBox in a dialog which is not part of a Dialog-based application.
Re: CDialog:ListBox in Doc/View
I've already told you what you're doing wrong.
Re: CDialog:ListBox in Doc/View
Quote:
Originally Posted by
crasher
D_Drmmr:
You seem not to have read the posts.
This is a doc/view app. The app reads in a few Mb from various data files (1 to many), and converts and displays the data. The user must be given the choice of which of the various data sets to display together. Therefore a dialog needs to be invoked, in order to present a listbox identifying the data sets which are in memory. All the data is at that point in the Doc. The ListBox has to present the choices, but obviously has to access the identities of the data sets. So I need to pass some info from the Doc to the Dialog. The data is not pre-existing in the dialog, and this is not a dialog app.
You wrote:
"You can ... pass the data from the doc to the dialog before you create it".
That is what I am trying to do, and that is the problem!
I can indeed write to an EditBox in the dialog which I instantiate in a Doc function, but when I try to get the Listbox to accept data (AddString(..)) in the same function, a Windows Assertion Failure is generated. Therefore the ClistBox appears not to have been initialised or its pointer is bent. So there is something tricky about a CListBox in a dialog which is not part of a Dialog-based application.
This doesn't have anything to do with the architecture. Most likely you are trying to access the CListBox.Addstring before calling the base class O
Code:
BOOL CMyDlg::OnInitDialog()
{
// BUG: Don't make any calls to child controls here
CDialog::OnInitDialog();
// Child controls initialized, okay to make controls here.
...
}
By the way, passing the data to the dialog is fairly simple. I usually just pass in a pointer to the document in the dialog's constructor.
Just add a parameter to the default dlg constructor that takes your specific app document pointer.
Re: CDialog:ListBox in Doc/View
Quote:
Originally Posted by
D_Drmmr
What's dreaded about GetDlgItem?
It's not that it's dreaded, it's just that using it results in extra code.
IMO, most folks use GetDlgItem because it's a carry over from coding in Win32 or they don't know how to create a Control variable in MFC.
The DDX mechanism hides the GetDlgItem implementation, when using data variables also handles the data transfer between the control and its associated variable.
Rather than having ugly GetDlgItem calls with casting everywhere, you can just use the dialog control variable.
Consider an edit box and say you want to hide it.
Create a control variable, and then...
Code:
m_EditBox.ShowWindow( SW_HIDE );
What about setting the edit box string?
Create a value variable (like CString), and then...
Code:
m_sEdit = _T("some text");
UpdateData( FALSE );
Compare that with:
Code:
CEdit* pBoxOne = (CEdit*) GetDlgItem(IDC_EDIT1);
pBoxOne->GetWindowText( _T("some text" );
Sure, you can argue that they're both 2 lines. But consider that UpdateData( ) only needs to be called once after setting any values - even for multiple controls.
Re: CDialog:ListBox in Doc/View
GCDEF, yes , thanks for the reminder. And I did notice that nice row of medals you've got there. Now, what about a suggestion, or is putting a usable listbox in a dialog called from a menu entry in a doc/view app really complex? If I knew the solution, I wouldn't be posting here btw.
Re: CDialog:ListBox in Doc/View
Quote:
Originally Posted by
crasher
GCDEF, yes , thanks for the reminder. And I did notice that nice row of medals you've got there. Now, what about a suggestion, or is putting a usable listbox in a dialog called from a menu entry in a doc/view app really complex? If I knew the solution, I wouldn't be posting here btw.
The point is the listbox has to exist when you add strings to it. It exists after the call in OnInitDialog to CDialog::OnInitDialog, and before CDialog::EndDialog gets called, which usually happens when you press the okay or cancel buttons.
Can you post a small snipped that shows how you're adding strings and from where.
1 Attachment(s)
Re: CDialog:ListBox in Doc/View
This is pretty basic. I've created a VC++ 2005 project to show one approach. This sample opens a test dialog from the view and passes in the pointer to the document. However, the test dialog could be as easily invoked from the mainframe or document as well.
I've added a m_sEdit variable to the document and a public GetEditString() public accessor method.
Code:
// Attributes
private:
CString m_sEdit;
// Operations
public:
CString& GetEditString( ) { return m_sEdit; };
When the OnViewTestdialog() menu handler in the view gets called, it creates a CTestDlg object and calls DoModal().
Code:
void CSdi2DlgView::OnViewTestdialog()
{
CTestDlg dlg( this, GetDocument( ) );
dlg.DoModal( );
}
The changes for the TestDlg are simple.
1) Pass in the CDocument ptr in the ctor
Code:
CTestDlg::CTestDlg(CWnd* pParent, CSdi2DlgDoc* pDocument)
: CDialog(CTestDlg::IDD, pParent)
, m_pDocument( pDocument )
, m_sEdit(_T(""))
{
}
2) Update the OnInitDialog() handler to initialize the local TestDlg
m_sEdit variable with the data from the document
Code:
BOOL CTestDlg::OnInitDialog( )
{
m_sEdit = m_pDocument->GetEditString( );
CDialog::OnInitDialog();
return 0;
}
3) Finally, save the data back to the document when the Ok button is pressed.
Code:
void CTestDlg::OnBnClickedOk()
{
UpdateData( TRUE );
m_pDocument->GetEditString( ) = m_sEdit;
OnOK();
}