CMFCButton::SetToolTip(str) not work in OnInitDialog() and - mfc

I want my CMFCButton to show tooltip when mouse over.
It doesn't work if I use SetToolTip() method in OnInitDialog
CMFCButton* bt = ((CMFCButton*)GetDlgItem(IDC_MFCBUTTON1));
bt->SetTooltip(_T("tooltip"));
BUT it does work if I put this code in message handle function like another button's click handle.
What I want is that the CMFCButton could show tooltip when the dialog is created, where should I put these code?
========================
By the way, The tooltip text I set in the Property view does not work for most time.

I just derived a class
class CMyButton : public CMFCButton
{
public:
void SetDelayFullTextTooltipSet(bool DelayFullTextTooltipSet)
{
m_bDelayFullTextTooltipSet = DelayFullTextTooltipSet;
}
};
Instead of a CMFCButton variable on the Dialog class I use the button, I now have a CMyButton.
And in the OnInitDialog, after the SetTooltip call, I do
button.SetDelayFullTextTooltipSet(FALSE);

Have you called the base class' OnInitDialog()? The main point is that the control needs to be created before you call SetToolTip() on it. Step into OnInitDialog() with the debugger and see if m_hWnd of the control has a value at the moment you call SetToolTip().

Related

Where to initialize a rich edit control on another dialog?

I have an MFC dialog based application that has 2 Dialogs: Main Dialog CMyDlgand Second dialog CMyDlg2.
On the main Dialog I add a Button "Go dialog 2". So I added a handler for the button so that when clicked it pops up the second dialog. Everything works fine But on the second Dialog I have added a Rich Edit Control from toolbox. I Added for it a variable. I also added a class for the second dialog.
Now If I run the Application I get the dialog one and if I pressed "Go to dialog 2" I got what I want. But I need at some point to change the font of the rich edit control but my program crashes.
So I overrided OnInitDialog and inside it do some changes to the control but program crashes. After debugging I found that the handle of rich edit is null?!
So how and where can I change the color or do some initializations to the control?
(I called AfxInitRichEdit2() in OnInitInstance())
BOOL CMyDlg2::OnInitDialog() {
m_richEdit.SetWindowText("Hello there!"); // program crashes because the handle m_richEdit is null.
return TRUE;
}
And this is the handler of button that creates the Dialog2 and that contains the rich edit control:
void CMyDlg::OnBnClickedButton1(){
CMyDlg2 theDlg;
theDlg.DoModal();
// TODO: Add your control notification handler code here
}
If I create the rich edit control programmatically then everything works fine because I create it at OnInitDialog and then it works fine but I need the one that is I added using the wizard toolbox.
*** The thing is that if I write:
m_richEdit.SetWindowText(""); // program crashes but if I wirte:
GetDlgItem(IDC_RICHEDIT221).SetWindowText(""); it works fine?
You probably have the following code inserted by wizard:
void DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_RICHEDIT22, m_richEdit);
}
This tells the dialog to associate m_richEdit with the dialog control IDC_RICHEDIT22. But this association is not performed until the base class method CDialog::OnInitDialog(); is called.
BOOL CMyDlg2::OnInitDialog()
{
//this line should work:
GetDlgItem(IDC_RICHEDIT22)->SetWindowText("Hello");
//this line won't work:
//m_richEdit.SetWindowText("Hello there!"); <- richedit's handle is NULL
//this line will subclass m_richEdit
//plus run other initialization
CDialog::OnInitDialog();
//m_richEdit is ready
m_richEdit.SetWindowText("Hello there!");
return TRUE;
}
It's recommended to put CDialog::OnInitDialog() int the first line, to make sure the initialization is done.
GetDlgItem works because the control IDC_RICHEDIT22 exists in the dialog template and you have a valid dialog handle. You are basically making a simple call based on WinAPI's GetDlgItem:
HWND hedit = ::GetDlgItem(m_hWnd, IDC_RICHEDIT22);
::SetWindowText(hedit, "Hello world");
There is no additional initialization needed.
But m_richEdit is just a C++ object, declared as CRichEditCtrl m_richEdit; The constructor for this C++ class doesn't do much besides setting m_hWnd to NULL.
Once it's associated with a valid window handle, we can begin using its windows methods such as CRichEdit::SetWindowText

Creating a control in MFC application

How to Create a Button using CButton class inside the Client area. I have just tried but the control is not getting displayed in the client area.
Code i used to create button
void CcontrolsView::OnDraw(CDC* pDC)
{
CcontrolsDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CButton cb;
cb.Create(BS_PUSHBUTTON,CRect(20,20,100,100),this,10000);
// TODO: add draw code for native data here
}
1 Don't do it in the OnDraw() method. Add a message handler for WM_CREATE in there, and instead do it in there.
2 Don't use a stack based object to create the button. I would suggest adding a CButton member to your view class called m_Button (or whatever).
In your CcontrolsView::OnCreate() method add code like:
m_Button.Create(BS_PUSHBUTTON|WS_VISIBLE|WS_CHILD,CRect(20,20,100,100),this,10000);
You need to construct a new CButton, then add it to the dialog by setting its parent or owner (I forget which - set both!) and other properties like a sensible position, and then call ShowWindow(SW_SHOW) on it.

Display the input of QLineEdit in a different window and or dialog?

I am writing a small QT gui application where there is a QLineEdit in my mainwindow.ui and I want to display the entered text in a separate dialog and or window when a button is pressed.
Now, I have stored the input in a variable, and I am also able to show this string on a label within this same mainwindow,
void MainWindow::on_GoButton_clicked()
{
QString mytext = ui->lineEdit_1->text();
ui->label_1->setText(mytext);
}
Now, I want to open a popup dialog (can be a window also), for example SecDialog;
SecDialog secdialog;
secdialog.setModal(true);
secdialog.exec();
and display the text of mainwindow->mytext string variable in a label of the SecDialog. How can I do that ??? I know it is a basic level question, but I think it will help clear lot of my doubts reagrding moving values of variables in between forms and classes.
Situation
So this is your situation:
From your code, the dialog is a modal dialog:
SecDialog secdialog;
//secdialog.setModal(true); // It's not needed since you already called exec(), and the
// dialog will be automatically set to be modal just like what
// document says in Chernobyl's answer
secdialog.exec();
Solution
To make the dialog display the text from the Window,
the concept is to pass the information(text) from the Window
to the dialog, and use a setter function from the dialog to display it.
Like Floris Velleman's answer, he passed the mytext string (by reference) to a customized dialog constructor and called the setter theStringInThisClass(myString) at once.
The implementation detail of this function is complemented by Chernobyl's answer (use the name setLabelText instead):
void SecDialog::setLabelText(QString str)
{
ui->label->setText(str); // this "ui" is the UI namespace of the dialog itself.
// If you create the dialog by designer, it's from dialog.ui
// Do not confuse with the ui from mainwindow.ui
}
Chernobyl suggested another way which calls the setter in the slot function and it bypasses the need of defining another constructor, but basically the concept is the same:
void MainWindow::on_GoButton_clicked()
{
QString mytext = ui->lineEdit_1->text();
ui->label_1->setText(mytext);
SecDialog secdialog;
secdialog.setLabelText(myText); // display the text in dialog
secdialog.exec();
}
Comment
I try to illustrate the concept as clear as possible, because from my previous experience on your question, you just "copy & paste" codes from answers and took them as your final solution, which is not right. So I hope this summary could help you understand the concept and then you may write your own code.
This task can be easy done with getter/setter method or with signal and slot, but setter is more suitable here. In SecDialog header:
public:
void setLabelText(QString str);
//in cpp
void SecDialog::setLabelText(QString str)
{
ui->label->setText(str);//it is label dialog
}
Usage:
secDialog.setLabelText(myText);
Also line where you set modal to true is not necessary because
This property holds whether show() should pop up the dialog as modal
or modeless. By default, this property is false and show() pops up the
dialog as modeless. Setting his property to true is equivalent to
setting QWidget::windowModality to Qt::ApplicationModal. exec()
ignores the value of this property and always pops up the dialog as
modal.
Assuming SecDialog is a custom class with an interface file as well you might want to pass it as a constructor argument or pass it by using another function.
So in the SecDialog constructor you could have something like:
SecDialog::SecDialog(QWidget* parent, const QString& myString)
: QDialog(parent),
theStringInThisClass(myString)
{}
And then you could call it like:
SecDialog secdialog(this, mytext);

How to update ToolTip text each time before display?

How can I make a toolTip text updating itself each time the tooltip is (about to be) displayed ?
I have a CDialog derived dialog which uses CToolTipCtrl tooltips in the usual way and it works just fine:
CToolTipCtrl member variable in my CDialog class.
created tooltip and added to tool in CDialog::OnInitDialog
message relayed to the CToolTipCtrl in CDialog::PreTranslateMessage
I also know how to update the toolTip text in various places of the code using CToolTipCtrl::UpdateTipText and CToolTipCtrl::Update
However, what I want and have not yet accomplished is this:
I want that the text of the tooltip updated each time the mouse hoovers over the tool before the according tooltip is displayed, i.e. the displayed text is dependent on the situation the moment the tooltip-text is displayed.
My working code so far (truncated to relevant lines):
class CmyDialog : public CDialog
{
virtual BOOL OnInitDialog();
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual void RefreshToolTipText(); // Want to call this prior each display
CToolTipCtrl m_toolTip;
}
BOOL CmyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_toolTip.Create(this);
m_toolTip.AddTool( GetDlgItem(IDC_SOMECONTROLID), "Sometext" );
m_toolTip.Activate( TRUE );
}
BOOL CmyDialog::PreTranslateMessage(MSG* pMsg)
{
if(IsWindow(m_toolTip.m_hWnd))
m_toolTip.RelayEvent(pMsg);
}
void CmyDialog::RefreshToolTipText()
{
m_toolTip.UpdateTipText( "updated runtime text", GetDlgItem(IDC_SOMECONTROLID) );
m_toolTip.Update();
}
When calling CToolTipCtrl::AddTool use the "special" value LPSTR_TEXTCALLBACK as the text to use for the tooltip. This will cause the tooltip to post a TTN_NEEDTEXT notification to the parent of the window you are adding a tooltip for. The parent can then set the text accordingly.
I seem to have figured it out myself. As I couldn't find the solution online, I'm going to post it here for references. I would still appreciate comments if there are any.
I've added the following line the message map of the CmyDialog class:
BEGIN_MESSAGE_MAP(CmyDialog, CDialog)
ON_NOTIFY( TTN_SHOW, 0, OnToolTipTextAboutToShow )
END_MESSAGE_MAP()
And I've added the following member function to CmyDialog:
void CmyDialog::OnToolTipTextAboutToShow(NMHDR * pNotifyStruct, LRESULT* result)
{
if ( pNotifyStruct->hwndFrom == m_toolTip.m_hWnd )
RefreshToolTipText();
}
Apparently, the TTN_SHOW notification code gets send via WM_NOTIFY each time a tooltip is about to be displayed. The if-check in my function checks that the toolTip is from the specific CToolTipCtrl.

TaskDialog with no buttons

Is it possible to show the TaskDialog with no buttons? I would like to be able to show just a progress bar (with a message), and then close the TaskDialog window when when my processing is complete (from the Timer event). Right now, I can show a disabled button and then call ButtonClick to close the window, but showing no buttons and having a CloseDialog method would be ideal.
Thanks.
Derive your own class from CTaskDialog
class CTaskDlg : public CTaskDialog
{
in CTaskDlg.h declare:
public:
void CloseTaskDlg(void);
protected:
HWND m_TaskDlgHwnd;
virtual HRESULT OnInit();
};
in CTaskDialog.cpp:
void CTaskDlg::CloseTaskDlg(void)
{
::SendMessage(m_TaskDlgHwnd, TDM_CLICK_BUTTON, static_cast<WPARAM>(TDCBF_OK_BUTTON), 0);
}
HRESULT CTaskDlg::OnInit()
{
m_TaskDlgHwnd = ::GetActiveWindow();
return S_OK;
}
CTaskDlg dlg;
dlg.CloseTaskDlg();
Both TaskDialog() and TaskDialogIndirect() force a default button if you do not specify any buttons, but you do have control over what kind of buttons are used, so I would place an Abort button in the dialog to cancel whatever operation you are displaying status of. Or maybe a Hide button if the user does not want to see the progress anymore without stopping the operation that is in progress.
You have to use TaskDialogIndirect() in order to activate the progress bar feature. You can also use its callback feature to obtain the HWND of the dialog so you can close it programmably when needed.
Otherwise, don't use the TaskDialog API. Just create your own window with your own UI, then you can do whatever you want with it.