I have added a CMFCOutlookBar control to a dialog. This outlookbar contains some 12 trees.
As per following link https://msdn.microsoft.com/en-us/library/bb983453.aspx
we can set active tab (in my case tree control) of our wish.
but it doesn't seems to work.
as per above link this function returns non zero value on success. Indeed it is returning 1 when i used it to set tree of my choice. but visually it's not changed.
can someone help me?
Problem solved.
CMFCOutlookBarTabCtrl::SetActiveTab() only works after window has been displayed.
I guess this is because CMFCOutlookBar stores it's previous state to registory and reloads on next run. And this overrides changes made by SetActiveTab(), if we use it before displaying of window.
I had the same problem, and you are correct that on load the tab gets set to the last session value - actually it seems to get set several times during the load process - some of them seem to correspond to each time a tab is added, and then the last time it is called seems to be the tab from the previous session.
The solution is to set the value once the window is ready to be shown. This can be done by overriding the OnShowWindow callback on the view which contains the tab bar.
In my case the tab bar is added in a view called MainFrame, which has a member variable CMFCOutlookBarTabCtrl* m_pOutlookBar; which is initialised in the OnCreate callback.
I can then correctly set the tab by overriding OnShowWindow to contain the following:
void MainFrame::OnShowWindow(BOOL bShow, UINT nStatus)
{
CFrameWndEx::OnShowWindow(bShow, nStatus);
if ((m_pOutlookBar != NULL) && bShow) {
//When the tab bar is shown, select the correctview
for (int tabIdx = 0; tabIdx < m_pOutlookBar->GetTabsNum(); tabIdx++) {
CString requiredLabel;
CString thisLabel;
requiredLabel.LoadString(IDS_OF_TAB); //The ID of the tab wanted
m_pOutlookBar->GetTabLabel(tabIdx,thisLabel);
if (requiredLabel.Compare(thisLabel) == 0) {
//If the tab label matches the one required
m_pOutlookBar->SetActiveTab(tabIdx); //set it as the active one.
break; //done.
}
}
}
}
Related
Ive made a search function for my List Control in Report View in my MFC Dialog. It looks like this
m_List.SetItemState((m_List.FindItem(&Finde)),LVIS_SELECTED,LVIS_SELECTED );
It searches the Content that is in the Variable Finde and marks it. Now it should mark the row. But I first have to click somewhere in the program. It doesnt mark the row directly after the function gets called.
Can anyone help me?
Here is the full function
LVFINDINFO Finde;
Finde.flags = LVFI_PARTIAL|LVFI_STRING;
Finde.psz = _T("Siffert");
if ((m_List.FindItem(&Finde)) != -1)
{
m_List.SetItemState((m_List.FindItem(&Finde)),LVIS_SELECTED,LVIS_SELECTED );
//m_List.SetSelectionMark((m_List.FindItem(&Finde)));
}
else
{
MessageBox(_T("No Results"));
}
You need to use the style LVS_SHOWSELALWAYS
Otheriwse the selection is only shown when the control has the focus and is active.
EDIT: Also keep in mind that there is also a LVIS_FOCUSED style, that also forces scrolling to this item.
I need to access the checkbox value in a different program. Check box is initiated in ToolDlg.cpp
DDX_Control(pDX, IDC_CalculateTBA, m_CalculateTBA);
in the oninitdialog initiated like this:
m_CalculateTBA.SetCheck(0);
CalculateAnalyticTBA = false;
void CToolDlg::OnBnClickedCheck3()
{
CalculateAnalyticTBA = m_CalculateTBA.GetCheck();
}
I need checkbox value in SetCal.cpp program. Here is the code i am trying in this program:
CToolDlg dialog;
if( dialog.CalculateAnalyticTBA )
{
Do some thing
}
But the dialog.CalculateAnalyticTBA is always tru even though i don't check the check box.
Plz let me know if you need any other info. Thanx for help.
It looks like you're creating a dialog, and never showing it. So CalculateAnalyticTBA has whatever value you gave it in the constructor of your CToolDlg class (or, if you didn't, whatever value the compiler gave it).
In order for this value to be set you must at least create the dialog so that the Data Exchange code (which invokes the DDX_Control and handles the binding of the checkbox and the variable) has a chance to run.
The correct way is to create and display the CToolDlg dialog and wait for the user to select his choices then only process the choices when the user finally click the "OK" button.
CToolDlg dialog;
// create and display the dialog
if (dialog.DoModal()==IDOK)
{ // user clicked the ok button, now do the work
....
}
Language: C++
Development Environment: Microsoft Visual C++
Libraries Used: MFC
Background: So I've created an application that is basically a large preference dialog where the user can configure a number of pages, each with a bunch of different settings. When the user is finished, he/she has three options for saving the preferences (as XML): Save Current [page], Save These, and Save All.
I'm working with the Save These function right now. When the user chooses this option, a dialog appears with check boxes for each page, allowing them to choose which pages they wish to output. Once they choose the directory into which they wish the files to be saved, the magic happens and the XML files are written.
Problem: I have a function (UpdatePageData) that will detect which page is being displayed and update the current page's data by calling UpdateData. I have put in a break point to watch to make sure the variables are being filled with the user's inputted values, and everything is dandy and working correctly. However, when the program jumps from the checkbox dialog (where I call UpdatePageData) to the classes for the pages I'm saving, suddenly all of the values are wrong.
Below I've included some code that will help you guys understand the program flow.
NOTE: In SaveThese, I am currently just working on saving a single page...I will have it update all pages selected once I figure out the problem I'm having.
Location: Main Dialog
void CSAPrefsDialog::OnSaveThese()
{
int msgboxID = ::MessageBox(
NULL,
(LPCSTR)"Are you sure you want to save?",
(LPCSTR)"Save These",
MB_ICONQUESTION | MB_OKCANCEL
);
switch (msgboxID)
{
case IDCANCEL:
break;
case IDOK:
UpdatePageData();
CSaveThese m_sT;
m_sT.DoModal();
break;
}
}
Location: Main Dialog
void CSAPrefsDialog::UpdatePageData()
{
if ((m_iCurPage >= 0) && (m_iCurPage < m_pages.GetSize()))
{
pageStruct *pPS = (pageStruct *)m_pages.GetAt(m_iCurPage);
if (pPS)
{
ASSERT(pPS->pDlg);
if (pPS->pDlg)
{
if (!pPS->pDlg->UpdateData()) // THIS WORKS. THE DATA IS UPDATED.
{
AfxMessageBox("Did not update data.");
}
}
}
}
}
Location: SaveThese Class
void CSaveThese::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE); // figures out which boxes are checked for saving
CDirDialog dir;
CSAPrefsDialog prefsDialog;
if(dir.DoBrowse())
{
prefsDialog.m_strDirectorySavePath = dir.m_strPath;
}
// [ other if-statements like the one below to check the check boxes ]
}
if(m_bST_FS)
{
FSC_Main m_FS;
m_FS.Save(prefsDialog.m_strDirectorySavePath);
}
OnOK();
}
Location: FSC_Main Class
void FSC_Main::Save(CString dirPath)
{
if(Validate())
{
dirPath += "\\FS_Config.xml";
FILE *fp = fopen(dirPath, "w+");
WriteXML(fp);
fclose(fp);
}
}
By the time it gets to WriteXML, the values have either reverted back to their initialized values (empty strings and -1 for all combo boxes), or have strange values (empty for strings, and large numbers for combo boxes).
I imagine I just have something in the wrong place. I'm just not sure why this is happening, and it's really the biggest hurdle between me and really getting this project rolling.
Jon, Very hard to understand your code. What i understood is you have a member variable m_pages in CSAPrefsDialog dialog class which you are updating by calling UpdateData. And then you are creating a local variable CSaveThese m_sT and calling DoModal. Do you expect CSaveThese class should hold the values of m_Pages?
I hope that I can explain my problem well enough for someone to help.
Basically, I have a horizontal scrollbar (ranged 0 to 1000) and an edit control that represents the position of the scrollbar divided by 1000, so that the user can use either the scrollbar to select a range of numbers between 0 and 1 up to a 3 decimal precision (.001, .002, ..., .987, etc.), or enter their own number in the edit box. As they scroll the scrollbar, the number in the edit control changes to reflect the new scroll position. When a new number is entered, the scrollbar sets itself to a new position reflecting the number entered. Meanwhile I also perform some calculations with this number as it changes (through either the scrollbar or the edit control) and display the results in another dialog.
Here is my problem: I'm having trouble deciding which event handlers to use to produce the proper behavior when a user enters a number into the edit control.
I'm using a double value variable called fuelMargin to handle my edit control and a CScrollBar control variable called fuelScroll to handle the scrollbar.
In my HSCROLL event I set the edit control to the scroll position / 1000. No problems there; when the user scrolls the scrollbar the edit box is correctly updated.
As for the edit box, my first attempt was an ONCHANGE event:
void MarginDlg::OnEnChangeFueledit()
{
CEdit* editBox;
editBox = (CEdit*)GetDlgItem(IDC_FUELEDIT);
CString editString;
editBox->GetWindowText(editString);
if (editString.Compare(".") != 0 && editString.Compare("0.") != 0
&& editString.Compare(".0") != 0 && editString.Compare("0.0") != 0
&& editString.Compare(".00") != 0 && editString.Compare("0.00") != 0)
{
UpdateData();
UpdateData(FALSE);
if (fuelMargin > 1)
{
UpdateData();
fuelMargin = 1;
UpdateData(FALSE);
}
if (fuelMargin < 0)
{
UpdateData();
fuelMargin = 0;
UpdateData(FALSE);
}
fuelScroll.SetScrollPos(int(fuelMargin*1000));
}
}
I needed that first if statement in there to keep from doing an UpdateData() when the user is trying to type a number like .5 or .05 or .005. It does produce a few wonky behaviors, though; when the user tries to type something like .56, after the .5 an UpdateData() is performed, the number becomes 0.5, and the cursor is moved to the far left, so if they tried to type .56 they would accidentally end up typing 60.5 -- which goes to 1, since I won't let them enter numbers lower than 0 or higher than 1. If they enter 0.56, however, this behavior is avoided.
For my second attempt, I commented out my ONCHANGE event and put in an ONKILLFOCUS event instead:
void MarginDlg::OnEnKillfocusFueledit()
{
UpdateData();
UpdateData(FALSE);
if (fuelMargin > 1)
{
UpdateData();
fuelMargin = 1;
UpdateData(FALSE);
}
if (fuelMargin < 0)
{
UpdateData();
fuelMargin = 0;
UpdateData(FALSE);
}
fuelScroll.SetScrollPos(int(fuelMargin*1000));
}
So now the user can finish typing their number and all is hunky dory--as long as they click out of the edit box. The scrollbar won't move and results won't be calculated until the box loses focus.
I want the results to be calculated as the numbers in the box are being typed; I want the scrollbar to move as the numbers are being typed. But I don't want typing to be disrupted, i.e. the actual numbers in the box changed or the cursor moved in any way.
Suggestions?
Thanks!
With the first approach, it looks like you're almost there: the only really significant problem is that the repeated calls to UpdateData() mess with the cursor position as the user is typing.
Given that you're trying to have a reasonably complex interaction between the controls, what I'd suggest is not to do validation in the OnChange() at all, so that as the user is typing he can type what he wants (which is how most numeric edit controls work anyway). When the user closes the dialog the controls are on (or clicks a button that uses the data in some way) then validation should be triggered, and a suitable error shown.
Once you're free from validating in OnChange(), you can fix the "cursor moves" problem by simply not calling UpdateData() in OnChange(). Instead, just parse the number from "editString" and, if it's in the valid range, update the scrollbar. That way, the scrollbar updates as the user types, and if they type in an invalid value the scrollbar stays put, and they'll get an error when they move to whatever the next stage is. Something like this (not tested):
void MarginDlg::OnEnChangeFueledit()
{
CString editString;
GetDlgItem(IDC_FUELEDIT)->GetWindowText(editString);
double editValue;
if ((sscanf(editString,"%lf",&editValue) == 1)
{
if (editValue >= 0.0) && (editValue <= 1.0))
fuelScroll.SetScrollPos(int(editValue*1000));
}
}
The only remaining important problem to note is that, if the user types some invalid value, or a number out of the valid range, then the edit control and the scrollbar will be out of sync. The simplest way to deal with that is to just decide that the edit control is the "master" value: that is, when we want to know what the user entered, we always look at the edit control, not the scrollbar, and validate data.
As for your second approach, one possible solution might be to implement a timer message handler: in the timer handler you could say the equivalent of "if the user hasn't typed anything for a second, I'll assume they're done, and parse the number and update the scrollbar". I'm not so keen on that as a solution, though.
I'd suggest watching for the Enter key, and performing UpdateData() then as well as OnKillFocus and OnChange. The more user-friendly the better.
Next, make sure your UpdateData() routine only works in the direction you need:
If ".5" is entered in the edit control, run your UpdateData() routine when the OnChange event is raised, but make sure to update your scrollbar only. Don't worry about updating the edit control to "0.5" until OnKillFocus is raised. Any updates in the reverse direction (to the edit control) will mess with your cursor. If your edit control is somehow bound to this double variable and auto updates when the var changes, consider leaving the double and your edit control seperate from each other until the OnKillFocus event is raised.
The same concept applies in the other direction as well. When the user scrolls, don't mess with the scrollbar. Just update the edit control and leave it at that.
I should add that XAML's data-binding features really help in situations like this, if you know how to use them properly. It's unfortunate for us native-type developers that it's so difficult to implement similar functionality using only event handlers.
I need to update a combobox with a new value so it changes the reflected text in it. The cleanest way to do this is after the comboboxhas been initialised and with a message.
So I am trying to craft a postmessage to the hwnd that contains the combobox.
So if I want to send a message to it, changing the currently selected item to the nth item, what would the postmessage look like?
I am guessing that it would involve ON_CBN_SELCHANGE, but I can't get it to work right.
You want ComboBox_SetCurSel:
ComboBox_SetCurSel(hWndCombo, n);
or if it's an MFC CComboBox control you can probably do:
m_combo.SetCurSel(2);
I would imagine if you're doing it manually you would also want SendMessage rather than PostMessage. CBN_SELCHANGE is the notification that the control sends back to you when the selection is changed.
Finally, you might want to add the c++ tag to this question.
A concise version:
const int index = 0;
m_comboBox.PostMessage(CBN_SELCHANGE, index);
What might be going wrong is the selection is being changed inside the selection change message handler, which result in another selection change message.
One way to get around this unwanted feedback loop is to add a sentinel to the select change message handler as shown below:
void onSelectChangeHandler(HWND hwnd)
{
static bool fInsideSelectChange = 0;
//-- ignore the change message if this function generated it
if (fInsideSelectChange == 0)
{
//-- turn on the sentinel
fInsideSelectChange = 1;
//-- make the selection changes as required
.....
//-- we are done so turn off the sentinel
fInsideSelectChange = 0;
}
}
if you fx want to change the title - which is the line shown when combobox is closed, then you can do following:
m_ComboBox.DeleteString(0); // first delete previous if any, 0 = visual string
m_ComboBox.AddString(_T("Hello there"));
put this in fx. in OnCloseupCombo - when event close a dropdownbox fires
ON_CBN_CLOSEUP(IDC_COMBO1, OnCloseupCombo)
This change is a new string not a selection of already assigned combobox elements