Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Adding method code to an "AddObject" object

Status
Not open for further replies.

Doug Lindauer

Programmer
Feb 2, 2017
38
US
I'm running VFP9 SP2 on a Windows 7 system.

I'm adding text boxes and check boxes to a container on a form at run time. I'm doing it this way, instead of hard coding the boxes because these boxes will be based on items in a table. The table can be edited by the user to accommodate future changes. The form and the container are created visually at design time using the Form Designer.

But I would like to utilize GotFocus and LostFocus methods on the check boxes. The reason I want to do this is that at runtime there is no indication that the check box has the focus. I know the first question that comes to mind is why doesn't the user just click the box with the mouse? Well the user requested a data entry screen where she doesn't have to take her hands off the keyboard and click around with a mouse.

But then I realized that, other than creating a custom class with predefined methods, I don't know how to add method code when you add check boxes like I'm doing. Seems like there should be a way but after reading Microsoft's programmer's guide on addobject, I'm not sure. So I'm hoping the VFP gurus here can help me out. If it can't be done without creating a class ... well that's just one more thing I'll have learned about VFP.

If people are curious, I'm including a little snippet from the code I'm using in the form's Init to create the check boxes. vCEACTS is a view which I step through:
Code:
    thisform.cntACTIVITIES.Height = max(150, 30 +30 * mCOUNT)
    mTOP  = 25
    mLEFT = 40
    for mCTR = 1 to mCOUNT
        mCONTROL= alltrim(vceacts.CONTROLNAME)
        mENABLED= empty(vceacts.PAIDAMOUNT)

        mNAME   = "chkPerm"+mCONTROL     && e.g. ' Paintball' becomes chkPermPAINT
        thisform.cntACTIVITIES.AddObject(mNAME,"CheckBox")
        with thisform.cntACTIVITIES.&mNAME
            .backcolor= thisform.cntACTIVITIES.BackColor
            .Visible  = .t.
            .height   = 25
            .width    = 25
            .Caption  = ""
            .top      = mTOP
            .left     = mLEFT  
            .FontBold = .t.
            .Alignment= 2
            .Value    = iif( empty(vceacts.PAIDAMOUNT), 0, 1)
            .Enabled  = mENABLED
            .DisabledForeColor = 0
            .DisabledBackColor = rgb(210,210,210)
        endwith 
        
        mTOP = mTOP +25
        skip
    next
 
Hi Doug,

You wrote "If it can't be done without creating a class ... well that's just one more thing I'll have learned about VFP." Do you have a strong reason for not wanting to create a class? It seems to me that that would be the easiest way of solving the problem.

Essentially, you would create a base checkbox class. You would set its properties to specify things like its height, width, alignment, and so on. And you would write the required code in its GotFocus and LostFocus methods.

In any case, even without the need to add method code at run time, creating your own classes as the basis for your controls is a very useful thing to do. I'd go as far as to say that, if you don't create your own classes (that is, if you base all your controls on the built-in classes), you will losing much of the benefit of VFP.

In fact, a better way to approach this problem might be to use VFP's event binding. That still involves creating your own classes, but it is a powerful feature that is worth the effort of learning. If that is of interest, you could start by reading Doug Hennig's paper on the subject: Raise a Little Event of Your Own.

Mike



__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi,

You may want to have a look at BINDEVENT().

Code of a small demo below

Code:
PUBLIC goForm

goForm = CREATEOBJECT("MyForm")
goForm.Show()

READ EVENTS

CLOSE ALL
CLEAR ALL 

**********

DEFINE CLASS MyForm as Form
	Width = 540
	Height = 360
	MinWidth = This.Width
	MinHeight = This.Height
	MaxWidth = This.Width
	Caption = "Sorting data in grid"
	AutoCenter = .T.
	ShowTips = .T.
	Themes = .F.


	ADD OBJECT grdNames as grdBase WITH ;
		RecordSource = "csrNames"
		
		PROCEDURE grdNames.Init()
			WITH This
				.Column1.Header1.Caption = "N°"
				.Column2.Header1.Caption = "Name"
				.Column3.Header1.Caption = "Age"
				.Column4.Header1.Caption = "G°"
				.Column5.Header1.Caption = "City"
				
			ENDWITH 
			
		ENDPROC 
		
		PROCEDURE grdNames.Refresh()
			LOCAL lcDBClick as String, lcClick as String
			
			lcDBClick = "DoubleClick header to reset order"
			lcClick = "Click header to sort by "
			
			DODEFAULT()

			WITH This
				.Column1.Header1.ToolTipText = IIF(ORDER() = "TNUMBER", lcDBClick, lcClick + "Number")
				.Column2.Header1.ToolTipText = IIF(ORDER() = "TNAME", lcDBClick, lcClick + "Name")
				.Column3.Header1.ToolTipText = IIF(ORDER() = "TAGE", lcDBClick, lcClick + "Age")
				.Column4.Header1.ToolTipText = "Sorting by Gender not available"
				.Column5.Header1.ToolTipText = IIF(ORDER() = "TCITY", lcDBClick, lcClick + "City")
				
			ENDWITH 
		
		ENDPROC 
	
	PROCEDURE Load()
		CREATE CURSOR csrNames (iRNumber I,cName C(20), iAge I, cGender C(1), cCity C(20))
		
			INSERT INTO csrNames VALUES (3,'Sahra', 31, "F", "Bruxelles")
			INSERT INTO csrNames VALUES (4,'Jeoffrey', 20, "M", "Paris")
			INSERT INTO csrNames VALUES (6,'Jenny', 53, "F", "Den Haag")
			INSERT INTO csrNames VALUES (1,'Toni', 44, "M", "Rome")
			INSERT INTO csrNames VALUES (2,'Sophie', 76, "F", "Berlin")
			INSERT INTO csrNames VALUES (7,'Jeremy', 67, "M", "New York")
			INSERT INTO csrNames VALUES (8,'John', 19, "M", "Chicago")
			INSERT INTO csrNames VALUES (9,'Marie-Josée', 28, "F", "Quebec")
			INSERT INTO csrNames VALUES (10,'Karen', 62, "F", "Toronto")
			INSERT INTO csrNames VALUES (11,'Abi', 56, "M", "Zurich")
			INSERT INTO csrNames VALUES (12,'Bernhard', 42, "M", "Basel")
			INSERT INTO csrNames VALUES (13,'Christiane', 26, "F", "Marseille")
			INSERT INTO csrNames VALUES (14,'Doris', 29, "F", "Munich")
			INSERT INTO csrNames VALUES (15,'Fred', 58, "M", "Wien")
			INSERT INTO csrNames VALUES (16,'Georges', 40, "M", "Budapest")
			INSERT INTO csrNames VALUES (17,'Juanita', 36, "F", "Mexico")
			INSERT INTO csrNames VALUES (18,'Jane', 52, "F", "Los Angeles")
			INSERT INTO csrNames VALUES (19,'Jan', 57, "M", "Amsterdam")
			INSERT INTO csrNames VALUES (20,'Henrietta', 41, "F", "Baton Rouge")
			INSERT INTO csrNames VALUES (5,'Mark', 54, "M", "Bern")
			
		INDEX on iRNumber TAG TNumber
		INDEX on cName TAG TName
		INDEX on iAge TAG TAge
		INDEX on cCity TAG TCity
		
		SET ORDER TO
		
		LOCATE
		 
	ENDPROC

	PROCEDURE Init()
		DODEFAULT() 
		This.DoBinds()
		
	ENDPROC
	
	PROCEDURE DoBinds()
		LOCAL loCol, loObj

		FOR EACH loCol IN This.grdNames.Columns
			IF loCol.BaseClass = "Column"
				UNBINDEVENTS(loCol)
				BINDEVENT(loCol,"MouseMove", This,"GetColumnToolTipText")
			ENDIF 
						
			FOR EACH loObj IN loCol.Objects
				IF loObj.Baseclass == "Header"
					UNBINDEVENTS(loObj)
					BINDEVENT(loObj,"Click",This,"HeaderClick")
					BINDEVENT(loObj,"DblClick",This,"HeaderTwiceClick")
					BINDEVENT(loObj,"MouseMove", This,"GetHeaderToolTipText")

				ENDIF
			NEXT 
		NEXT
		
	ENDPROC

	PROCEDURE HeaderClick()
		LOCAL ARRAY laEvents[1]
		LOCAL lcOrderBy, loObject
		
		AEVENTS(laEvents, 0)

*!*			= MESSAGEBOX("Clicked: " + SYS(1272, laEvents[1]), 64, "Clicked Object", 1500)
			
		loObject = laEvents[1]

		lcOrderBy = SUBSTR(RIGHT(SYS(1272, loObject),9),1,1)

		IF INLIST(lcOrderBy, "1", "2", "3", "5")
			WITH This.grdNames
				.SetAll("FontBold", .F., "Header")
				.SetAll("FontItalic", .F., "Header")
				.SetAll("BackColor", RGB(228, 228, 228), "Header")
			ENDWITH 

			WITH loObject
				.FontBold = .T.
				.FontItalic = .T.
				.Backcolor = RGB(0, 201, 201)
			ENDWITH 
		ENDIF 

		DO CASE 
			CASE lcOrderBy = "1"
				SET ORDER TO TNumber
					
			CASE lcOrderBy = "2"
				SET ORDER TO TName
					
			CASE lcOrderBy = "3"
				SET ORDER TO TAge
				
			CASE lcOrderBy = "4"
				= MESSAGEBOX("Sorting by Gender is not available", 64, "Setting Order", 2000)

			CASE lcOrderBy = "5"
				SET ORDER TO TCity

			OTHERWISE
				= MESSAGEBOX("Call developper", 16, "Setting Order", 5000)

		ENDCASE

		LOCATE  
		This.Refresh()
		
	ENDPROC

	PROCEDURE HeaderTwiceClick()
			WITH This.grdNames
				.SetAll("FontBold", .F., "Header")
				.SetAll("FontItalic", .F., "Header")
				.SetAll("BackColor", RGB(228, 228, 228), "Header")
			ENDWITH 
			
			SET ORDER TO
			LOCATE
			This.Refresh()

	ENDPROC 

	PROCEDURE GetHeaderToolTipText(nButton, nShift, nXCoord, nYCoord)
		LOCAL laEvents[1]
		LOCAL loObject
		
		AEVENTS(laEvents,0)
		
		loObject = laEvents[1]
		
		This.grdNames.cToolTipText = loObject.ToolTipText
		
	ENDPROC
	
*!*	The procedure below AVOIDS the TTT to show when the mouse is hovering over columns	
	
	PROCEDURE GetColumnToolTipText(nButton, nShift, nXCoord, nYCoord)
*!*			LOCAL laEvents[1]
*!*			LOCAL loObject
*!*			
*!*			AEVENTS(laEvents,0)
*!*			
*!*			loObject = laEvents[1]
*!*			
*!*			This.grdNames.cToolTipText = loObject.Name

		This.grdNames.cToolTipText = ""
		
	ENDPROC
	
	PROCEDURE Destroy()
		ThisForm.Release()
		CLEAR EVENTS

	ENDPROC
ENDDEFINE 

DEFINE CLASS grdBase as Grid 
	cToolTipText = ""

	Top = 6
	Left = 6
	Width = 540 - 12
	Height = 360 - 12
	Anchor = 15
	BackColor = RGB(0, 246, 246)
	ColumnCount = -1
	
	PROCEDURE ToolTipText_Access()
		RETURN This.cToolTipText
	ENDPROC

ENDDEFINE 

**********

hth

MarK
 
I second Mike, the easiest way is to define classes and add those. To not need to change all data you could take names of controls like "checkbox" and change them to "_checkbox" or whatever all your default control classes will be named by a prefix or suffix. In the future maybe you offer some control classes with extra functionality. You don't need to ask users to program OOP or use new names for the controls, as you're in control about what class name is really used.

BINDEVENTS is somewhat OK, but you only really need it when the code you want to bind to also is generated at runtime. Although even then you don't need Bindevents. You could create a PRG with a class definition embedding that code and use the class you define there, VFP can COMPILE at runtime (and does so whenever you call or DO a PRG instead of an FXP and other such things, mostly evaluating expressions with EVAL() or using EXECSCRIPT(), not to forget macro expansion. These all are reasons you get a runtime compiler for free with VFP runtimes.

Bindevent is a good solution when you have a form with native controls you wish to extend without hacking the VCX or SCX, but as you are creating the controls at runtime, you have the better option to add specific classes, so why reject that? It saves an extra step every BINDEVENT needs to do when the (empty) method of a native control is called or an event happens, plus Bindevent sometimes explicitly needs some code in the method or event to be triggered and to also call the delegate object. Which is something you also need to create, and then you can also create a class.

Personally, I think BINDEVENT is used with a bit too much pride about such an advanced feature. Using BINDEVENT as your latest brand new and most advanced tool for adding a code layer is like only having a hammer and only seeing nails in every problem to fix.

BINDEVENT is only really helpful where you can't write classes or subclasses. Binding to COM objects, for example. Though for that specific scenario there also is EVENTHANDLER(). The other very good feature of BINDEVENTS is binding to Windows Events aka Windows Messages.

Okay, your specific problem is with focus. Which control has focus, currently? Well, that's _screen.ActiveForm.ActiveControl, so no need to write any event handling, such a general reference already exists. A bit of caution is good because the active form can have no active control, for example when all controls are set to Enabled=.F., so check whether Vartype(_screen.activeform) is 'O' (object) and then whether Vartype(_screen.activeform.activecontrol) is 'O'.

Also, you can SetFocus(). And besides that, for what reason do you need to know whether the focus is on a checkbox? The native checkbox already will be clicked by using the SPACE key, there's nothing you need to program for users not needing the mouse, they can change focus by TAB or BACKTAB and there are some hotkeys like SPACE=Click without any code of special classes.

And on top of all that you also have access keys:
Chriss
 
All,
Thanks much for the replies.

First let me answer Chris's question about needing to know where the focus is. As far as I can see, there's no cursor pointer for a check box. So how does she know which check box her space bar is going to activate? There will be several of them. So I'm thinking that the gotfocus code will maybe make the box bigger or turn color. Something like that so she'll know where she is.

OK, I guess my reluctance to create new classes might seem strange. I'm an electrical engineer by education, but got thrown into database development by my CO when I was a Navy LT back around 1980. (I'm a Vietnam era vet.) He wanted me to do something with this Z80 computer system my predecessor bought. So I taught myself how to use dBase 2.3b to write an inventory system for the command. Since then I've done a couple of dozen systems in the dBase, Clipper, and FoxPro world in addition to working with Oracle, Sybase, MySQL with PHP and now VFP. Getting into database system development is like falling into quicksand. You never get out.

I still think that the OOP thing is the greatest impediment to productivity that could possibly have been devised by deranged men. The learning curve is very steep. But I think VFP strikes the right balance between procedural coding and the OOP mindset. And it incorporates a decent SQL implementation in addition to being blazing fast. And it lets you create Windows visual systems. And it's dBase: one language for DDL, DML, and user interface coding. But I've got 35 years of system development using procedural code behind me and only about 5 years of fiddling with OOP oriented systems. It's all self taught although I do periodically take graduate level information systems courses. Maybe before I turn 90 I'll get my Master's degree. :) I've done 4 or 5 small projects with VFP and this current one is my first big one with close to 100 tables and views. (I've written a couple of simple Java apps for my Android phone too.) But I know nothing about Windows API's, Active X controls, COM objects, etc. I don't even know what those things are, really. I learned long ago that I can't know everything and database stuff is enough for me. Things like event binding and event handlers seem to be an extra complexity I'd be better off avoiding. I read Henning's paper. Cool, but for the moment I'll go with the idea of writing the custom checkbox method.

My mindset is always to make my code as plain vanilla as possible. I do that because I know that the idiot who comes along in 6 months or a year won't know what I'm doing otherwise. Sticking with plain vanilla makes the learning curve on the system easier for that idiot. And usually, that idiot is me. (What the heck is my code doing here? What is that???) And I don't know about you guys but as for me I can say that no matter how many comments I include, I never seem to have enough later on.

So to make a long explanation shorter, that's why I'm slow to create anything not in the basic VFP world. A database system is (often) complicated enough without making the end user interface coding hard to troubleshoot. But I'm not an anti-oop Nazi. Matter of fact I'm going to take Mike and Chris's advice and just go ahead, bite the bullet and make a custom check box class. I already have one custom command button class, one text box class and have to grudgingly admit that there's a good justification for one more on this same screen.

Apologies for running on. It's just that this system I'm working on, database-wise, is far more complicated than anything I've ever worked on and I'm just one guy on a project that could justify several good people. So on the user interface side, I want to make things as simple as possible for myself.

Thanks to all for such excellent and thoughtful responses.



 
Sorry to hear that, but I respect your decision.

Just to add focus shows on a checkbox:

checkboxfocus_vwwt8g.png


The caption gets a dotted frame and becomes bold. Using a font with a better distinction of normal vs bold will help. Do your checkboxes have no caption?




Chriss
 
I still think that the OOP thing is the greatest impediment to productivity that could possibly have been devised by deranged men.

You will be pleased to know that you are not alone. I have come across many programmers over the years who share that view - although they don't always express it so strongly.

But I have come across far more programmers who take exactly the opposite view. In my long experience as a VFP instructor, I find that, of the developers who are initially hostile to OOP, the majority change their view when they see the mystique stripped away.

I won't try to change your mind about OOP, but perhaps you will allow me to show you how easy it will be to use it to solve your problem:

1. Start a new form in the Form Designer.

2. Add a checkbox. Set the properties that you are interested in (Height, Width, FontBold, Alignment, etc). Write the required code in the GotFocus and LostFocus.

3. Click on the checkbox, then go to File -> Save As Class. In the resulting dialogue, select "Selected control"; enter a name for the class (let's say chkActivity); enter a name for a file, that is, the class library, to store the class (let's say CustomClasses - don't worry about the extension). You have now created your first class library.

4. Discard the form. You won't need it again.

5. In your application, execute SET CLASSLIB TO CustomClasses (or whatever filename you chose). If that file is not in the VFP search path, precede it with the path name. You could execute that command at the start of the main program, or in the Load event of the form that is to hold the checkboxes.

6. In the code that you posted above, change the second parameter of the AddObject() from "Checkbox" to "chkActivity" (or whatever you called the class). In the same code, remove the settings of Height, Width, FontBodl, Alignment, etc. (as these are now inherited from the class).

7. Carry on with the rest of the development. If you need to make any changes to the checkbox, open it in the class designer, make the changes, and save it. You won't need to change your code in any way - that is one of the great advantages of OOP.

Give it a try. You might be pleased with the result.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Assume you give Mikes' procedure of creating classes a try, if you name your controls _checkbox, _textbox, _editbox, _listbox, etc. then all you need to change in your code is

Code:
mCONTROL= [highlight #FCE94F]"_"+[/highlight]alltrim(vceacts.CONTROLNAME)

Provided you SET CLASSLIB in your main.prg, which you also don't need when everything finally is in an EXE.

All this does is creating your base controls instead of the native controls, and any behaviour you'd like to change in any control is easily done in the library of your base controls. You don't need to change your metadata about what controls are added to a form at runtime, etc., it's just always prefixed with "_" or whatever prefix you prefer.

And later you can offer customers special controls you give special names, so when they define future forms, they can use them, as long as they became part of your EXE (or separate VCX libraries). Just remember they all need the same prefix in their name. And to make third party controls available, well, you subclass them with your prefix, too.

Regarding the look of the checkbox, a thing that bugs me, too, is the checkbox itself is only coming in one size fits all. You can change to graphical mode and use your own checkbox images for checked/unchecked/disabled. That doesn't have a separate image for focused, but you can change the pictures in gotfocus and reset them on lostfocus and then you can do anything you want in images.

Chriss
 
Some quick comments.

First off, if I were given a project and could choose anyone I wanted to be essential team members, I'd grab Mike, Chris and someone like Tamar. Give me a couple of million and I could probably enlist anyone. :) You guys know all the stuff I don't know about VFP and interfacing with things like Office and Windows. But I would do most of the system analysis and requirements along with being responsible for the logical model. Among other skill sets, I'm a CA certified logical modeling specialist using ERwin. (The physical model implementation is a modification of the logical model and a good tool like ERwin seamlessly connects the two.)

And as an aside I'm a strong advocate of a proper model and modeling tool being the foundation of any system. The model isn't some afterthought you put together later. It's essential to development and no changes should be made in the database without being first incorporated into the model where every table, column, index, and relation are clearly defined. Then changes can be forward engineered into the development system. This way, everyone knows what everything is for all the time. If the model doesn't come first then it's always out of date and people can't depend on it. I'll get off my soap box now.

My focus is first and foremost on the data being correct and allowing the user to easily enter good data and getting the needed outputs. So I'm a little weak on advanced UI issues in VFP. But I won't ignore your suggestions. I just have to proceed at my snail's pace in learning them.

Chris: yep. I was not using the caption on the check box BUT your question made me realize that that was the way to go, especially since I'm creating another label object with the Activity name. How stupid! :) I should just have used the Activity name as the checkbox's caption. I went down this road because I was adapting a grid to just be an independent collection of boxes in a container. Of course, that's pretty much what a grid is anyway. And I have been giving some thought to that. There are two other containers I'm making on this screen but maybe I can just stick with the grid for the activities container. But it's not all bad because this pushed me to expand my thinking (with help from my friends) on using objects and learning more about VFP interface issues.

Mike, I don't think I understand the need to do set classlib. I realize that I've already done some of what you're suggesting. I guess I've made some class libraries just by stumbling around doing stuff. I've done it 2 ways I guess without quite realizing the difference. One way is I've included a couple of classes in a procedure file like this:
Code:
define class RegBox as TextBox      && Registration text boxes in form REGTRANS
    procedure LostFocus
        this.Parent.Value = REGSUM(this.Parent)
        this.Parent.LostFocus       && force the parent container to update its totals textbox every time a RegBox changes its value
    endproc         
enddefine

*-------------------------------------------------------------------------------------
function REGSUM    && sum text boxes in containers on form REGTRANS
*-------------------------------------------------------------------------------------
    parameters oCONT, ;
               pONLYENABLED     && Only sum enabled text boxes.  Useful in getting totals which are not already in TRANSACTIONS
    local mCTR      ,;
          mVALUE    ,;
          mSUMTHIS              && Decide whether to include in the sum

    mVALUE = 0
    for mCTR = 1 to ocont.ControlCount
        mSUMTHIS = .not.pONLYENABLED .or.ocont.Controls(mCTR).Enabled   && if it's not enabled and ONLY ENABLED is passed, then this item came from those TRANSACTIONS which are already entered and are not new transactions entered on this screen
        if upper(ocont.Controls(mCTR).Name) = 'TXT' .and. mSUMTHIS      
            mVALUE = mVALUE +val(ocont.Controls(mCTR).Value)
        endif
    next

    return  mVALUE

The other way is that I click on the Class tab in project manager and create a new class, usually a container, and then put stuff in it. Then to include it on a form I drag it from the list. Here's a screen shot of the form as it stands now and project manager showing my classes. I've never had to use set classlib to make it work. It just works.
REGTRANS_d9pyz7.png


This is just a work in progress so there's a lot of temporary stuff on it. The user wants to be able to quickly enter multiple transactions related to one camper's camp registration in one fell swoop. Ok. I keep telling her I can do anything I want. Just give me enough time to figure it out. I've already figured out how get the system to modify a Word document template and fill in entries from the database and then save them as PDF's and eMail them. I know very little about using VBA on Word documents and next to nothing about SMTP and how to send emails. But there's examples out there and I followed them. You guys could probably do it a lot slicker than I've done it but I'm sure I can't afford you. :)
 
Doug,

First, thanks for your kind words.

Regarding SET CLASSLIB: In your first example, it's difficult for me to know for sure without seeing all the code, but I suspect the reason that you don't need SET CLASSLIB here is that you are defining the class in a PRG, and that PRG is already open on the class stack at the point where you use the class. This is different from the step-by-step procedure that I posted, because there I created the class visually and stored it in a VCX (CustomClasses in my example). At the point at which you use the class (your ADDOBJECT() calls), VFP has to know where to find the class.

In your second example, it is the act of dragging the class from the project and dropping it on the form that tells VFP where to find the class. So, again, you don't need the SET CLASSLIB in that case.

Apart from that, it's looks like you are on the right track with the two examples that you posted. I encourage you to persevere with this.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Doug Lindauer said:
I should just have used the Activity name as the checkbox's caption.

Yes, and you can choose whether the caption is left or right of the checkbox. There is a good reason to use an extra label, too: You can rotate it. Which is nice to have, if you want many checkboxes aligned horizontally, not vertically. The caption then can go 45° or 30° in the up and right direction. And the hotkey labeling I mentioned earlier will mean setting focus to the label will set focus to it's checkbox, just give it the next tab order.

I haven't tried how the focus shows in that case.

Your idea of code in the focus events isn't bad at all, you could use gdiplus to draw a highlighted rectangle in the size of the control, you could generalize a concept of having an extra focussable marker left of any control, use the graphical style or other feature. Which makes it a non-standard UI in Windows terms, but can be something users can easily get used to.

The idea of an extra marker could look like an LED that's lit for the active control, so the form looks like an electronic device. The style will depend on the application. I just think successful software in the music genre looks like a mixer or other studio device for good reasons. It fits into the real hardware of a studio.

I'm with you in database design, that's old school and I also don't mind a code-first approach, but not for the price of a bad database design.

Chriss
 
Looks like you're on the way to solving your problem, but I want to add a little about why to use classes. You've seen here a reason to use them for specialized controls, but the big reason to use classes is to make things work the same throughout.

When you create your own first-level subclasses of the VFP base classes, you can put in all the stuff that's really going to be the same throughout the application. That might be visual stuff like font and size. It might be behavioral stuff; I'm working on an application where my client wants textboxes to go reverse video when they get focus, so the user can always easily see which control has focus. At the form-level, you might have code that needs to run on the way in or out of each form. And so on and so forth.

When you use classes, you don't have to worry about remembering to do the same things for every form or for every control.

For people who've worked in the procedural world, OOP is a change of mindset. But it's worth learning because it's a huge productivity booster once you do.

I don't mean this to be offensive, but to me, the analogy to using VFP without creating classes is for someone 30 years ago to be using FoxPro or Pascal or some other procedural language without using procedures and functions, insisting that they have to write all the code in one big program. Yes, subroutines requiring learning, but once you master them, they make you far more productive. OOP is the same way.

Tamar
 
I'll be honest. I can understan your position in the way the current paradigm change is towards functional programming languages, and I didn't made that move and I think I won't. Simply because OOP is good enough for business applications. I like some of the details of enhanced OOP as C# offers it, or also a step "back" to C++, currently C++20. Though VFP has some ideal features and I can do without some fancy stuff.

Indeed since the UI standard became flat VFP can compete again, it's 3D look controls went to flat design by the simple black frame around the white textbox area, for example, that was giving VFP an extended life, too. And itÄs for good anyway, as it means a cleaner and clearer interface for users. It's funny to look back to complaints like this:
Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top