|
Hi,
I have a small problem with my radio buttons. I have a group of three buttons, when I click button 0 everythings fine, but if I click button 1 or 2 and special requirements are not met the software should give a message to the user and reselect button 0. I did it in the BN_CLICKED message handler of the two radio buttons like this:
void CDlgMacroColor::OnReferenceDynamic()
{
if(some condition)
{
m_EditColorRefThreshold.EnableWindow(TRUE);
}
else
{
AfxMessageBox("No Global Reference found! Changing to Local Color Reference.",MB_OK|MB_ICONEXCLAMATION,0);
m_EditColorRefThreshold.EnableWindow(FALSE);
m_iReferenceType = 0;
UpdateData(FALSE);
}
}
m_iReferenceType is the integer determining which radio button is selected.
The problem that occurs is, that with UpdateData(FALSE) a new BN_CLICKED message is sent to the same radio button I just declined (I would accept it if it was sent to the new one, but no, it's sent to the old one) and the user gets the message box twice.
Now my two questions: Why does this happen? and even more important: How can I avoid this?
Thanks in advance
Martin Dietz
|
|
|
|
|
m.dietz wrote: Why does this happen?
You are triggering an event from an event handler.
It sounds to me that if (some condition) is false then the radio buttons that represent invalid options should be "disabled". Then you would not need check the condition and act from inside the event handler.
|
|
|
|
|
Disabling the button is not possible, as the condition that defines whether this selection is allowed cannot be tested at OnInitDialog(). Anyway even if I found a way to test it there, the software requirement specification orders this interface behaviour (Maybe our product managers think that the user is not able to determine by his own how to enable that button if I disable it, and that's why he has to be told when trying to select this option... ).
That I trigger an event within an event handler should not be that unusual, I also use UpdateData(FALSE) in OnInitDialog() and UpdateData(TRUE) in OnOK(), well, I didn't test if those event routines also are called twice, but I don't think so. As I said, I would understand it if I got an BN_CLICKED on the new radio button, because changing the button in the GUI by UpdateData() is like clicking on it, but I get this event on the old button again and that's what irritates me.
|
|
|
|
|
m.dietz wrote: Disabling the button is not possible
Of course it is possible
m.dietz wrote: (Maybe our product managers think that the user is not able to determine by his own how to enable that button if I disable it, and that's why he has to be told when trying to select this option... ).
Sorry, I am a software developer not a psychiatrist. I can't provide mental health solutions for you. Even when the radio button is disabled there can still be a tooltip and/or "help" that would explain "how" to enable it. This would be a superior solution to the one you describe and if you did it and demonstrated it to "the managers" and they still reject it then I would start working on my resume.
|
|
|
|
|
m.dietz wrote: The problem that occurs is, that with UpdateData(FALSE)...
Another reason why UpdateData() should be avoided, especially with radio buttons. Assign a CButton object to each radio button, and use the SetCheck() method.
"A good athlete is the result of a good and worthy opponent." - David Crow
"To have a respect for ourselves guides our morals; to have deference for others governs our manners." - Laurence Sterne
|
|
|
|
|
So you say that's a bug of UpdateData()? OK, I could assign an interface object for each radio button and set it by SetCheck() and get the value of it by GetCheck(), but apart from being that a big effort to uncheck every other option each time I want to check one option, doesn't that undermine the idea of having one value for the whole group? I would do that only as a last resort, and I don't think the situation is that desperate, there must be a way how Microsoft thought UpdataData should be used making a sense.
|
|
|
|
|
m.dietz wrote: So you say that's a bug of UpdateData()?
That's not what I said at all. See here for more.
"A good athlete is the result of a good and worthy opponent." - David Crow
"To have a respect for ourselves guides our morals; to have deference for others governs our manners." - Laurence Sterne
|
|
|
|
|
OK, I read this article, and I disagree in more than one point.
The main reason why it says not to use UpdateData() is because this could mess up the original data. This can easily be avoided by keeping the original data stored in some variables/some structure, where it is read from by OnInitDialog() or some Init function you programmed yourself. Second I agree with the critic of NEVER using member variables of a function outside the class they are members of. Information handling between the dialog and the caller can be much easier achieved by giving a pointer to a memory location via a dialog function like InitData(). In this structure the caller writes its initialization data, OnInitDialog() reads from that structure to initialize the variables, in OnOK() the dialog writes back the result and the caller can read from the structure what he had in mind. This creates an usage of the dialog like
<br />
CMyDialog dlg;<br />
MYStructure str;<br />
str.Var1 = info1;<br />
str.Var2 = info2;<br />
dlg.MyInit(&str)<br />
dlg.DoModal();<br />
info1 = str.Var1;<br />
result = str.VarResult;<br />
In preserving the data in that structure you also always have a copy of your data for a Reset() which itself can call a function InitControls() shared with the OnInitDialog() routine. All intermediate data SHALL NOT BE STORED IN VARIABLES THE CALLER HAS ACCESS TO.
Second reason, why I disagree with that article: It says UpdateData would force you to keep an eye on your state and make sure it is always consistent and not a mixture of old and new data. That also can be avoided by keeping the separation between dialog data and display data. The only time a consistency check for display data has to be made is at the OnOK() event before writing the results back to the structure I used above. At every intermediate point it is not necessary to keep consistency, as all changes of UpdateData() only affects display data, not dialog data and when it is all checked at the OnOK() there still is time enough to tell the user what he did wrong (e.g "You can't choose 'dynamical selection' without selecting a rule.").
Third point of the article is the order in which the ON_CHANGE notifications happen. If there are some dependent data fields, the author says, then you get problems with setting the correct data of control B in accordance to the value of control A when the message is handled for control A. The author seemed to forget that it's YOU, the programmer, who knows about this dependencies, and YOU can set the values before calling UpdateData() making sure that the only work the ON_CHANGE event handler has to do is check that the data entered into control B is correct.
And to my humble opinon it's far easier to remember whether to use UpdateData with TRUE or FALSE than to remember each control and keep its value in an extra variable.
But this article does still not explain, why I get an ON_CLICK message for a control that is not clicked instead of an ON_CHANGE, which I can ignore if I want, and therefore, I still think this extra ON_CLICK message is neither wanted nor documented and something that is neither documented nor expected is a misbehaviour of software and as long as there are not really good reasons for this misbehaviour I see this as a caused by a bug.
|
|
|
|
|
You can fix this problem by putting this at the very beginning of your OnReferenceDynamic() function:
if (m_iReferenceType == 0)
return;
|
|
|
|
|
Sorry that doesn't work! Coming from the standard choice "local reference" at the beginning of the event handler m_iReferenceType is always 0. This I already tried on Friday.
Edit: It works if I set a UpdateData(TRUE) as the first line, but I don't know which unwanted events that wil bring...
-- modified at 3:51 Monday 6th August, 2007
|
|
|
|
|
If I have an integer value, and I want to write its value into a string, how do I work out how many characters the integer will take up?
For example, if I have the number 100, and I want to put it into a string using sprintf, how do I dynamically work out how many characters the number will take up (in this case 3)?
Thanks for your help!
--PerspX
"Nowadays, security guys break the Mac every single day. Every single day, they come out with a total exploit, your machine can be taken over totally. I dare anybody to do that once a month on the Windows machine." - Bill Gates
|
|
|
|
|
Why would you need that ?
|
|
|
|
|
I have a string of text displaying the size of a file (in bytes) in the form "File Size: %d bytes" (where %d is the file size) and I would like to allocate enough memory whatever the file size.
--PerspX
"Nowadays, security guys break the Mac every single day. Every single day, they come out with a total exploit, your machine can be taken over totally. I dare anybody to do that once a month on the Windows machine." - Bill Gates
|
|
|
|
|
Perspx wrote: I have a string of text displaying the size of a file (in bytes) in the form "File Size: %d bytes" (where %d is the file size) and I would like to allocate enough memory whatever the file size.
DWORD dwFileSize = 123456;
char s[28];
sprintf(s, "File Size: %lu bytes", dwFileSize);
"A good athlete is the result of a good and worthy opponent." - David Crow
"To have a respect for ourselves guides our morals; to have deference for others governs our manners." - Laurence Sterne
|
|
|
|
|
To know how many characters in the formatted string are needed to allocate,
use _vscprintf an then use new or malloc with the number of characters returned.
|
|
|
|
|
Perspx wrote: how do I work out how many characters the integer will take up?
Why bother? Assume the worst case and allocate room for 10, or 20 if you are on a 64-bit machine.
"A good athlete is the result of a good and worthy opponent." - David Crow
"To have a respect for ourselves guides our morals; to have deference for others governs our manners." - Laurence Sterne
|
|
|
|
|
That's bad programming and a waste of resources.
--PerspX
"Nowadays, security guys break the Mac every single day. Every single day, they come out with a total exploit, your machine can be taken over totally. I dare anybody to do that once a month on the Windows machine." - Bill Gates
|
|
|
|
|
Perspx wrote: That's bad programming and a waste of resources.
"A good athlete is the result of a good and worthy opponent." - David Crow
"To have a respect for ourselves guides our morals; to have deference for others governs our manners." - Laurence Sterne
|
|
|
|
|
You can laugh but I bet that the reason that most Microsoft programs are slow and clunky is because the programmers who write them do bad programming techniques such as that.. and it doesn't matter for that particular example, as that only wastes 19 bytes at the maximum.. but if you apply that technique for other data allocation, then you could waste a lot more which makes your application unreliable and not perform so well..
--PerspX
"Nowadays, security guys break the Mac every single day. Every single day, they come out with a total exploit, your machine can be taken over totally. I dare anybody to do that once a month on the Windows machine." - Bill Gates
|
|
|
|
|
Please continue!
It's amazing!
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
|
|
|
|
|
Perspx wrote: I bet that the reason that most Microsoft programs are slow and clunky...
I highly doubt that. Reserving 1 byte or 20 bytes from the stack will make no noticeable difference in your code's execution time, but using the log10() function and asking the memory manager for countless small amounts of memory will.
Consider the following:
for (int x = 0; x < nFileCount; x++)
{
int nLength = (int) (log10(dwFileSize) + 1.0);
char *s = new char[18 + nLength];
sprintf(s, "File Size: %lu bytes", dwFileSize);
delete [] s;
} compared to:
char s[28];
for (int x = 0; x < nFileCount; x++)
{
sprintf(s, "File Size: %lu bytes", dwFileSize);
}
"A good athlete is the result of a good and worthy opponent." - David Crow
"To have a respect for ourselves guides our morals; to have deference for others governs our manners." - Laurence Sterne
|
|
|
|
|
Perspx wrote: That's bad programming and a waste of resources.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
|
|
|
|
|
Is that a laugh at me or in agreement?
--PerspX
"Nowadays, security guys break the Mac every single day. Every single day, they come out with a total exploit, your machine can be taken over totally. I dare anybody to do that once a month on the Windows machine." - Bill Gates
|
|
|
|
|
To solve that problem mathematically: take the logarithm to base 10 (double log10(double))and add 1.
Else you can format it into a CString and check that length.
Greetings,
Martin
edit: sorry, used the wrong log function (base e instead of base 10)
-- modified at 10:22 Friday 3rd August, 2007
|
|
|
|
|
m.dietz wrote: To solve that problem mathematically: take the logarithm to base 10 (double log10(double))and add 1.
First make sure the value is greater than zero.
m.dietz wrote: Else you can format it into a CString and check that length.
the above works anyway.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
|
|
|
|