Hi.

Who here already made a masked edit control like the one from VB...
I would like to do it in Win32 API but it sounds a bit like problems when dealing with copy/paste etc...

In clear, I would like to do something like : ##-##-##-##-## to allow inputs like 01-02-03-04-05, without of course having the user care about the '-' characters : would be nice for phone numbers.
Filtering the forbidden chars is easy but formatting them in a nice way...

I have found some interesting ressources, but they are all MFC, as usual.

I would be interested if somebody has an interesting source or documents on which I can base my work on.

Thanks !
Posted on 2003-07-08 08:37:43 by JCP
The format string directs a state machine on what to do with the input characters. All messages sent to the edit control could be sent to a hidden edit control and then the string of text from the hidden control is processed by the state machine and displayed in the user edit control. This way all the function of cut-n-paste is preserved without actually pasting to the user control.
Posted on 2003-07-08 09:15:42 by bitRAKE
This is from http://www.catch22.uk.net/ but i attached the source.

"Rename to .RAR"
Posted on 2003-07-08 13:49:04 by cakmak
cakmak,

Thanks for the link : I already knew this source. It is nice to prevent the suer to type some forbidden characters in the control, but it is unfortunaly not enough for what I want to do.

What I would like to do is something like the maskedit.ocx from Visual Basic : you define a mask and this masks prevents the forbidden chars o be input, and reformat the string in the edit control.

In clear : something like this... http://www.codeguru.com/editctrl/masked_edit3_demo.zip there is the source but MFC... :-/
It is a good start anyway, but I don't know the framework at all.

BitRAKE,
Thanks but I think I am missing something about your theory, but in my opinion, there should be a way to do it with only one edit control...
Posted on 2003-07-08 15:59:49 by JCP
You could subclassing an edit control and make a new control as Dll with this.
This new control will communicate with parent window by sending and receiving
messages.

Manos.
Posted on 2003-07-09 13:59:40 by Anonymous
Ya, the MFC is doing this. SImply subclassing the edit controls (both of them). Then it has a background OOP class to do the dirty work on the control. I will try to convert it to masm for ya. (this topic accidently spawned an entirely new direction in my OOP model, which will save alot of memory, so i want to finish it first)...

:NaN:
Posted on 2003-07-11 00:25:54 by NaN
Common problems I have with these kinds of controls in programs I have used:

1. Can not cut/paste values in all formats:

example: (___) ___-____ {USA telephone number}

I should be able to cut/paste in all logical formats: 1231231234 or (123) 123-1234, and possibly pieces of text (like last four digits).


2. Pasting more characters than the control allows is not possible or causes an error. All that is needed is to truncate the string of digits. This is very common when cut-n-pasting from serveral applications.



...if someone is going to create this type of control, please keep the user in mind and create a very flexible interface. Over the years I have done much data entry - struggling with some of the most limiting and illogical interfaces than I care to remember. :)
Posted on 2003-07-11 01:40:23 by bitRAKE
Well, it was a lot of extra work that im happy to have now (Nobjects.inc and CString.asm), but the end goal has arrived.

I set out to help you with this over a week ago, and out of necessity coded up everything in between. But now things are good and writting this source for you was fairly easy to do because of it (took a day).

Below is my own source. I borrowed only the RSRC file from your MFC example. I origionally tried to translate it to masm, and then realized MFC is full of spegetti code and needless classes and class methods. In turn i wrote my own source based on its idea. Im pretty happy with it. Its only one source file, not too long, and far simpler that the crap in the MFC source.

It works by subclassing, however, i designed the source to work like API's. Allowing you to 'convert' any number of controls at once, and each control will have its own independant memory (much like objects ;) ) for its masks and text data.

I advise reading the readme file for the break down of functions to call. I have also provided the example translated in MASM.

Please let me know what you think. I think it shows the success of my new NOjects.inc model.
:alright:
:NaN:

PS: It also supporst clipboard pasting of text data. However the data must be in the condensed form.

Attachment Deleted by NaN
Posted on 2003-07-19 17:11:28 by NaN
NaN,

Thanks for this impressive work !
I hope you had not done this only for me. :)

I don't know if it is a bug or a feature but when using mask (for example : ##-##-##-##-##, a typical french phone/fax number) and when the user inputs the numbers, exemple (01-02-03-04-05) the '0' from "02" is eaten by the '-' character... this doesn't happen in the MFC version.
Well, I don't know if I am clear :)

Anyway, it is not a complain :)
Thank you very much for it. :alright:
Posted on 2003-07-20 06:05:27 by JCP
Ah I see.. well here is a fixed version. I cleaned up the WM_CHAR code. (Looked a bit spegetti like and needed a bit of pollishing anyways. Your concern just gave me an excuse to do so ;)

I only modified this section. Everything else is the same. I did however add another private helper function at the end.

This version will stop on any char that is improperly given, and will ignor non-mask chars if the next mask char in sequence fits the char your trying to type (as your asking for).

Hope you like!
PS: I enjoy these sorta projects, so its not a burden to me at al ;) . Actually from a design point of view it was a totally new idea. I subclassed the control, and then took it over and ran it like an object. Basically inserting private memory to the object for the mask and text. This design approach allows the same proc handler to work for multiple edit controls that are converted, yet at the same time ensure that each is unque and may have different text/masks for each! I never did this before from a subclassing point of view. I do it all the time when I make custom controls though! So in this view, it was a unique project to take on :alright:

:alright:
NaN

Attachment Deleted by NaN
Posted on 2003-07-20 12:09:13 by NaN
Wow !

Thanks for this ! It is already nearly perfect ! :-)
The mask problem is solved and is already very nice for the use I want to do of it (phone and fax number user input).
I've noticed some things that I think could be improved though.

In your version of the control, the user can not delete a char using the del key. Only using back space. The MFC control can, but it is in a confusing way. I had mixed feelings about how this MFC control deals with the del key, but after all, I think it is not that bad (it sets the caret on the next masked character automatically).
It would be nice to put such a functionality in the assembly control. :-)

Pasting on the control do funny things.
Using the "condensed form" string (only with the significant masked characters : the one with '#' or '?'), it works, but the first character of the pasted string is not pasted ('_' instead).
Pasting using the "formatted form" (example : pasting "01-02-03-04-05" on a ##-##-##-##-## masked control) do strange things.
Using Ctrl+V does not work to paste (I assume it is because WM_CHAR).
But dealing with pasting on such a control look kinda hard... so maybe it is simpler to disable pasting on the control ?
Anyway, as I said, it is already very good as it is.

Well, I didn't had time to dig into your source yet. Sorry.
You said that each control has it owns memory. Does it requires some freeing up ? (I think it would need if it is some sort of dynamically allocated memory that has its pointer stored in the GWL_USERDATA part of the control, but I didn't looked in the source yet and don't know your object model either).

Thanks again for this !

(Sorry accidently clicked EDIT instead of QUOTE and didnt realize until i submitted - NaN)
Posted on 2003-07-21 01:34:46 by JCP
In your version of the control, the user can not delete a char using the del key. Only using back space. The MFC control can, but it is in a confusing way. I had mixed feelings about how this MFC control deals with the del key, but after all, I think it is not that bad (it sets the caret on the next masked character automatically).
It would be nice to put such a functionality in the assembly control. :-)


Now your getting into an whole other bag of apples. M$ doesnt return the del key as a CHAR message. It appears in the KEYDOWN and UP messages (as does all the char messages too). This is why its not handled. Actually to keep the annoyency factor down, i simple catch the del, and ignore it by returning. I personally dont want to get into that boat, it would just be annoying. But if you want to go for it. The starting point is at the WM_KEYDOWN message routine.. from there you will have to write your own behavior. You can probably cut&paste a good portion of it from the WM_CHAR handler which does 90% of the bahavior.
Pasting on the control do funny things. :)
Using the "condensed form" string (only with the significant masked characters : the one with '#' or '?'), it works, but the first character of the pasted string is not pasted ('_' instead).
Pasting using the "formatted form" (example : pasting "01-02-03-04-05" on a ##-##-##-##-## masked control) do strange things.

Ya, i didnt modify this. I only gave you a starting point there. BitRake pointed out that it should be *magical* and i really didnt want to get into that either (not a magician i guess). I realize it might be a bit wonky. Especially with the changes you aske for earlier. If you want to correct that to, i would be happy to answer questions along the way.
Using Ctrl+V does not work to paste (I assume it is because WM_CHAR).
But dealing with pasting on such a control look kinda hard... so maybe it is simpler to disable pasting on the control ?

Yes, exactly. I didnt want to get into this too much.

Well, I didn't had time to dig into your source yet. Sorry.
You said that each control has it owns memory. Does it requires some freeing up ? (I think it would need if it is some sort of dynamically allocated memory that has its pointer stored in the GWL_USERDATA part of the control, but I didn't looked in the source yet and don't know your object model either).


It does insert dynamically heap memory to each sub-classed control. It does this with an extra manual call to WM_CREATE. Remember that the control is already created by the time you sub-class it! So i get away with doing it here!. You dont need to worry about freeing resources. Its handled by windows when the parent window is closed. It in turn sends a WM_DESTROY message to all child windows. This is where i take care and free the memory.

As for my OOP model. Its not to hard to understand. Its designed this time around to be very readable and user friendly. Its almost like reading Pseudo code, i would be surprised if your having a hard time here.

Best of luck! If you have questions as you tailor it to your specific needs feel free to ask!
:alright:
NaN

Thanks again for this !
Posted on 2003-07-21 16:08:09 by NaN
Ok, thanks your help.

I think that for now, it is indeed better to prevent pasting in the control...
About Delete key handling, I will implement it once I get some time (I have a professionnal trip to London in 3 weeks and I have a bunch of things to do to prepare it during this time), and I will post the new version here.

Now, I have a problem, I use your control in a dialog which is like an address book (employee database).
On each edit control change, it updates the employee record with the edit control text. It works.

But, when I read the information and set it the control, it is not displayed properly : using ##-##-##-##-## mask with a "0145002236" displays : _1-45-__-22-36 on the control.

I use your lib on a C app, using the .lib file you provided :

Called on WM_INITDIALOG


mb_LoadString(hInstLang, &memAlpha, IDSTR_PHONE_FORMAT); // "##-##-##-##-##"

ConvertToMask(GetDlgItem(hDlg, IDE_PHONE));
SetEditMask(GetDlgItem(hDlg, IDE_PHONE), memAlpha.pointer);


Called on click on a button :


GetOperatorInfo(hDB, OperatorID, &memAlpha, OPERATOR_PHONE); // custom function (memAlpha is an object from my string library that prevents buffer overflows and memAlpha.pointer a "char *")
SetEditText(GetDlgItem(hDlg, IDE_PHONE), memAlpha.pointer);



What am I not doing right please ? Is there some precautions to take when using SetEditText functions...
Btw, here is how I prototyped the functions :



#include <windows.h>

void _stdcall ConvertToMask(HWND hEdit);
void _stdcall SetEditMask(HWND hEdit, char *szMask);
char *_stdcall GetEditMask(HWND hEdit);
void _stdcall SetEditText(HWND hEdit, char *szText);
char *_stdcall GetEditText(HWND hEdit);
BOOL _stdcall IsFieldsFull(HWND hEdit);



Thanks.
Posted on 2003-07-23 01:33:03 by JCP
I didnt do a full check again, when i cleaned up the WM_CHAR code (and included the '-' hopping).

I will test it again, its probably a mild oversight on my behalf, cause i didnt change anything critical in the source, i only cleaned and restructured the code better.

I assumed nothing would be affected by it, but It appears i was obviously wrong.... Will post a correction probably tonight.

PS: Your C++ source looks ok to me.

Regards,
NaN
Posted on 2003-07-23 16:47:21 by NaN
Something must be up with your C++ source, cause it works fine for me. However, it did spot a bug. I had the compare routine go ".if eax > 30h " so numer '0' was ignored as a valid char. This was fixed, however, i didnt need to change anything else in the lib.

I recompiled another dialog box which has another button on it to show the proper working of SetEditText().

For The mask of "Province: ??, Phone: (###)/###-####"

The Set Text is: "AB1234567800"

And the shown result in the control is: "Province: AB, Phone: (123)/456-7800"

This is how it was designed. You can save smaller, more condenced data, and inflate it when showing it via the control.

Hope this helps!
Attached it the same thing with the corrected 30h problem.
:NaN:

Deleted by NaN
Posted on 2003-07-23 17:31:53 by NaN

Something must be up with your C++ source, cause it works fine for me. However, it did spot a bug. I had the compare routine go ".if eax > 30h " so numer '0' was ignored as a valid char. This was fixed, however, i didnt need to change anything else in the lib.


NaN,

Thank you very much for this...
Looking at the example of string I posted, the missing characters using SetEditText() were indeed '0' and your fix of course did the trick ! :alright:

Now, I have another little problem...
As I said, at update of the edit control, I update the database with the control contents...
It is done by handling a notification message (sent by the edit control) in the DlgProc (EN_UPDATE)...
When I exit the window... for some reason, the subclassed edit control seems to erase the contents, and thus the dialog receives the EN_UPDATE notification message...
Do you see any reason why it could happen ? I think it maybe resets the text on the control when you do the cleaning up since it does not do this with the other classic edit controls I have on this dialog ?

I will try to fix it myself by checking with Spy++ and dig in your source code...

Thanks.
Posted on 2003-07-24 01:21:39 by JCP
Well, I fixed the problem from the caller code but it is very strange :confused:...
Anyway, it was in fact on dialog opening the problem was, not on its closing...

I have a static variable to say if I want to handle the EN_UPDATE notification or not... it was initialized to FALSE on its definition...
Reinitializing it with FALSE in WM_INITDIALOG did the trick, but I don't know how its value could have ever changed before WM_INITDIALOG...
But, oh well... :)
Posted on 2003-07-24 02:12:01 by JCP
Im confused, and mildly concerned.

Keep in mind i have heap memory generated via a WM_CREATE message to the sub-classed edit control. Since the control is already created, i sub-classed this message as a new, next stage, init. However, if for some reason you dialog is sending this message again (a third time ~ one to create, two for my subclassing, and three for what ever reason the parent window has) you will have extra heap memory not being freed and forgotten (the second call's allocation).

Now, i might be missunderstanding your problem. But if not, this is something to pay attention to. As an alternate, and probably safer, you can rename the WM_CREATE to a WM_USER+300 or something. But im personally surprised that more than one WM_CREATE message would get sent to any control.

As for your EN_UPDATE messages, i did nothing directly to this from sub-classing. I simply pass on anything im not interested with to the normal handler.

*brain fart* :: I just realized im not properly handling the OldEditProc variable properly for multiple subclassing. Its being used as the sole global variable. This means if, for some reason, two edit controls have different origional handlers, i would be routine *all* forwarded messages to the last edit control that was 'converted'. I fixed this now in this next version! As well as modify the WM_CREATE to WM_USER+206.

The above is probably paranioa anyways.. im not 100% sure, but i believe windows uses the same proc for all edit's. This is most likely not your bug (but its good to fix anyways ;) ).

To get EN_UPDATE messages, when your not expecting them may come from WM_SETSEL or WM_SETTEXT which i use in the subclassing. But they are only active when you hit a key in the control. (Or a key message is issued to the control). So im still not sure where this is comming from. My only suggestion is to compile a debug version of the lib, with PrintText messages to proove to yourself that certain sub-classing fragments are been entered when no key is hit by a user. (I use VKIM's debug macros heavily for this sorta stuf).

:NaN:
Posted on 2003-07-24 19:30:30 by NaN

But im personally surprised that more than one WM_CREATE message would get sent to any control.

As far as I know, WM_CREATE is never sent. When you call CreateWindowEx, Windows just calls your window procedure with that message, even before your program reaches the message loop (you can verify this with a debugger). That means SendMessage, etc. simply can't send that message... or at least I could never manage to do so. SendMessage doesn't complain, it just silently does not do anything.
Posted on 2003-07-25 14:29:23 by QvasiModo
Hi QvasiModo


As far as I know, WM_CREATE is never sent. When you call CreateWindowEx, Windows just calls your window procedure with that message, even before your program reaches the message loop (you can verify this with a debugger). That means SendMessage, etc. simply can't send that message... or at least I could never manage to do so. SendMessage doesn't complain, it just silently does not do anything.


Well SendMessage works fine I used my color button and just put a messagebox into it
Attached the demo...

Clickin on the button send the message

Cya Greetz Kahn
Posted on 2003-07-25 17:27:21 by KahnAbyss