INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

Controls "outside" the Form

Controls "outside" the Form

(OP)
So there are a number of discussions we've been having here, that seem to all be starting to tie together.
First, the elimination of PUBLIC (which I totally get is a good thing, and I'm committed to doing that).
Second the ability to print RTF in report form (Using Mike Gagnon's approach) or Office Automation (using Tamar's approach).
Which brings me to the third part -- building out the RTF capability, given the "RIBBON" bar that I've created within the MAIN form.

I was looking at the VFP Samples where they have an example of RTF, and example that I built from some other app years ago, where I stopped because I couldn't print it.
But both of these examples have the controls embedded in the form, and I want to be able to use them globally. (So any form open, will use the same controls when editing RTF editbox).

So when it's in the form, it's easy... the control and the ole box both see each other, and using the .ActiveControl property of the form, it's easy enough to get the ole field to respond to change in the RTF tools.

But, now my main form contains the "Ribbon Bar" that I've built. That bar is a pageframe inside the MAIN form as well. When I open a new window inside it, I can't reference Main.Pageframe1.basepage2.rtfControls.rtfFontSelect... To my OLE control in window CLIENTS which then has a pageframe within it, and pages, and ... down until I get to the RTF control (in one case, there is a pageframe on the page of another pagefram).

So all this deep traversing between forms which don't really see each other... to have a common set of controls like RTF font control, what is the best way to tackle this?

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

If you were using a toolbar, this would be easy because toolbars don't get focus, so you can use _SCREEN.ActiveForm to find the form with focus and then operate on it.

But I gather you're using controls in a form, which means that your controls will get focus. (I haven't paid much attention to the RTF thread because I haven't worked with RTF.) The general solution is to track which actual form has focus. In general, this is something a framework does for you.

The good news is that this is something you can build into your forms and your application object. Put code in the Activate method of your form "base" class to call an application object method that stores a reference to the currently active form in a property of the application object. That is, the Activate method has code like:

CODE

goApp.ActivateForm(This) 

and the application object's ActivateForm method has something like:

CODE

LPARAMETER oForm

This.oActiveForm = m.oForm 

You'll want some error-checking in there, but that should give you the idea. Once you have this, you can find access the active form with goApp.oActiveForm. (I'll add that when I do this, I actually have corresponding code in Deactivate to clear the setting, but because you have controls in your top-level form, you can't do that. In fact, you'll need special code in that top-level form to make sure it never registers itself as the active form.)

Tamar

RE: Controls "outside" the Form

So let me get this straight: You dont really ask about RTF or OLE, the core problem is getting at a control from the man form ribbon, whereas the control is not on the main form. So you talk about communication between forms.

Forms knowing each other has many simple solutions. They all base on the principle, you don't need to know something, you only need to know someone/something else knowing what you need to know. In short loose coupling.

1. goApp.oFormhandler instantiates all forms and knows references to them. If anyone would ask the handler for a reference to a form, the handler could provide it. So the ribbon would call into goApp.oFormhandler.getreference("someform"), for example.
2. You can make use of a host/guest or publisher/subscriber pattern. Any form could register itself as publisher or subscriber to messages. So if there is a form with RTF control you want to access from main forms ribbon, you can turn around your need or want and say the RTF control form wants to subscribe to any messages about handling it's RTF control, making itself controllable this way.
3. Again and again, your communication can also happen via data, via DBFs, eg the "Bold" button is clicked in the ribbon, you save the setting bold=.t. into some table and the form with the RTF control reads it from there, you neither need the bold button to act on the RTF control nor the RTF control see the status of the bold button.

I also often give the example mail automation does not have a call to sendbutton.click(), but to mail.send(), think about it. You don't act on the frontend, the frontend is just the interface to the backend, the model, having the essential info, the frontend is just for the human in front, but as the developer you should not program in the mindset of the user seeing the interface, you define the classes with the functionalities you want to use and you define the calls to do to get something done. Then the interface just makes these calls.

You only think about direct solutions, as they seem simpler. But they have one big disadvantage, they mean an entanglement. If you only program in a direct way, you can only act on controls on the same form, yes. That makes it bad code, as it binds things together and you can't separate them.

Going back to the mail example: Is every computer directly connected to any other computer to send a mail by putting it on the target computer? No, there is a protocol and mail servers receive and forward the mails, there are intermediate stations and indirection.

It only seems more complex, but every single entity only has to do quite simple and easy to understand things. The complexity results from the collaboration of the single components.

Bye, Olaf.

RE: Controls "outside" the Form

(OP)
Hi Tamar and Olaf,
Yes, you're quite right, at the base level this is about communicating between forms. It's just the RTF example is the first time I've ever needed to do this, but as I build out the ribbon bar, I suspect this will crop up in other areas where I want to interact with forms and that ribbon form class. Thanks for the clarity and description you've put together Olaf, it makes it much clearer in my head. And I figured the goApp object is going to play in this. I've almost got the RTF edit box working and interacting with the controls I've built (in another simple form where it's just them, so I could play with getting the edit functions to work as desired first).
Once I'm done with that, I'm going to dive into the goApp implementation. I think I've got a better understanding now of it. I'll start with this, and my glTipState (getting rid of it as a PUBLIC) as my entry point, and then expand it from there.
So hopefully quite for the next couple of days as I work this out, and then maybe more questions as I get stumped. :)
Many thanks.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

Scott, I'm not sure if this is what you are asking, but I'll tell you how I handle this.

In my base form class, I have property named oCaller, which holds an object reference to the calling form. This means that any form can access the properties and methods of the form that calls it, and of the controls on the calling form.

So if your main form holds the ribbon, and a subsidiary form needs to access a control on the ribbon, it can do so by refering to THISFORM.oCaller.oRibbon.SomeProperty.

Note that this approach does not need any changes to your existing code. To store the object reference in oCaller, you just add this line to the Load event of the base form class:

THISFORM.oCaller = _SCREEN.ActiveForm

This works because, during the Load event of a given form, that form is not yet active. The previous form (that is, the one calling the current form) is still the active form.

Does that help at all?

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads

RE: Controls "outside" the Form

(OP)
Hi Mike,
I think I understand your point. Let me try it, and see if I'm able to get the behavior that I'm expecting out of it.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
Hi Mike,
I completely forgot you had mentioned this when discussed before (as I wound up spending about a week extra getting some ribbon behavior to work the way I needed, which took a lot longer than I expected, but it was worth giving it the time, because it's flawless now).

So, if I understand this I add the property oCaller to the baseclass form (lowest level), so even the TopLevel form and the form called from it will have this property?

And does it matter what they default setting of oCaller is? 0, .F., ""?
I'm giving it a try now, if this works, it will save me huge amounts of plinking around.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
Hi Mike,
WOW! Not that I doubted you, but I am still delighted when something that feels like a magical "leap of faith" works!
So, here's my next question, because it gets to ba bit of work here...
In my example, I have the RTF controls embedded in a page in a frame on the basepage.
So my call from the RTF control back to the ribbon controls is a bit ugly and looks like this:

ThisForm.oCaller.pgfRibbonBar.Pagebase2.RTFFormatting.CBOFontName1.Value = This.SelFontName

So what this does, is passes the font name of the test in the control back to the RTF controls so that it is aligned to that of the test in the box. (Or default font value if the box is empty). When I clikced in the box, it updated my combo box control on the ribbon, so that was terribly exciting.

The concern I have is, all this "Path" back to the control is needed: ThisForm.oCaller.pgfRibbonBar.Pagebase2.RTFFormating.CBOFontName1 (and I have about a dozen RTF controls built onto the Ribbon so far, and I'm expecting to add in a few more, so this is more like standard "Office" type text control... this makes it very familiar to my user for how to make great text features).

Is there some way that is best to control this kind of thing? It feels like the "pagefram and page" make for long addresses. If it's the only way, I will make it work, but thought there might be something more elegant I'm missing. What I love about this Mike, and I'm so grateful... I thought I was going to have to make some very complex transitions and transformations to goAPP and then I didn't know how I was going to get goAPP to let the calling form know about changes at the child form level. This is like super magic to me. Very Very cool.
Thanks! I wish I could star this 10 times.


One thing I just encountered too though is, this is fine going "one way". From the child form back to the Top Level, but what about when I need to send some update back to the child?
For instance, in my RTF example, slicking in the RTF Control box on the child form, it automatically updates the font and size (and other attributes that I have the RTF control set to update, like bold and italic). But if I go to the Ribbon now, with text selected in the RTF field on the child table, and I change the state of Bold or the font from Ariel to Segeo UI, how do I tell it which control on the child form that is to be used? In the Samples example, it utilized the .ActiveControl. property of ThisForm. I haven't tried that yet, but my thought was that it won't work.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
Hi Mike,
So this seems to have worked from the Called Form BACK to the Top Level form, but not from that form back to the child form.

So what I have is:
Top Level Form calls = Data Edit
Data Edit form has some edit boxes on it, 2 RTF, 2 Test and 1 text box.

DataEdit.oCaller can see all the properties of TopLevel
But TopLevel, doesn't see DataEdit at all. (Other than as something like "WONTOP").


There is an interaction I'm trying to make work here with the RTF Controls, so "backward" it works, but then not forward.

If there is text in the RTF box, that is maybe Bold and has a color Green as properties, when you select that text, the RTF controls at the TopLevel form refelct that (the color selector turns Green and the Font Name switches to the font of that text, and the BOLD attribute shows active).

Now if with the text selected When I go to the RTF controls the "Selection" seems to go away (but if you click back on the form, it does show that its still selected). But if I then try to turn bold off, I get a message about values not existing (related to calling that object back). The RTF demo has something like ThisForm.ActiveControl.SelBold = This.Value (or something like that...)

So from what I can tell, I'm "communicating" back to the parent form, but it's not "communicating" back to the child, because the references don't exist. It seems oCaller is "one-way"?

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

Mikes suggestion always only works one way, yes, because only one form is the caller form and the other is called. The caller form can know what form it called even more easily, though, as it calls it, it will have a reference to it in the moment of generating it, so you can put that to the process of creating a child form. I'd add another oCalledForm property. Even though The ribbon form will never have a caller form, it's the root form always existing, you could reuse it's oCaller property to store the child form reference, but it feels wrong, doesn't it? If you find another name for the oCaller property that could work.

But I'd go back to what I suggested earlier, and not work with such form properties. Make a form handler knowing form references and give it methods to ask for a from reference and you don't have to feed and set any properties as your memory of form references.

Besides that, as the ribbon form is always existing and its reference never will change, you can add its reference to a property like goApp.oRobbon, Simply in the Init() of the ribbon you can then set goApp.oRibbon = THIS and everywhere you need access to the ribbon form, you can reference goApp.oRibbon. Remember the discussion about goApp and the "big ball of gum"?

Mikes suggested mechanism is for differing sets of parent/child forms. For ribbon/any active form the situation is easier, you don't have a varying parent form, so make use of that.

The ribbon knowing all it's child forms is harder than just storing one reference, Since clicking in the ribbon makes it the _Screen.ActiveForm, unlike a toolbar, which does not change this, you can't reference it via this system property. It comes back to a form handler knowing all forms and being able to tell you references and for the ribbon to effect all relevant forms when you make some general setting like font size or language, it also comes back to the topic of host/guest or publisher/subscriber.

If you publish a newspaper you also don't call every subscriber to read out the new articles of it, you publish the paper and every subscriber gets it. In the example of a language setting in the ribbon, for example, you let a drop-down list combo box be bound to a list of languages each with their language ID and then have the controlsource goApp.iLangugeID. Everyone interested does not read the ...pgfRibbonBar.PagebaseX.REgionAndLanguage.CBOLanguage.Value, but the goApp.iLanguageID, and everybody interested simply uses the ability to be informed about property changes via BINDEVENT(): A form does BINDEVENT(goApp,"iLanguageID",THISFORM,"SetLanguage") Now everytime goApp.iLangaugeID changes anybody subscribed to this property is informed and can read the new ID value in a method of its own choice. So the ribbon does not need to know who's reacting or who it needs to act on, it simply cares for the simple concern to "publish" what is the current language choice.

This whole concept then also gets rid of acting on any deep paths. Neither the ribbon should need to know paths of child forms to act on, nor any form should know how the ribbon is structured. The core thing you want to transport is information, not controls.

Bye, Olaf.

RE: Controls "outside" the Form

(OP)
Olaf,
I think I'm following you on this. I'm going to try your suggestion with goAPP.oRibbon and see if it allows me to do what I need, I think in conjunction with oCaller (for one way, and oRibbon for the other). Will let you know if I run into trouble, or how the solution works.
Thanks.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

Scott,

Let me answer your earlier questions:

Quote:


So, if I understand this I add the property oCaller to the baseclass form (lowest level), so even the TopLevel form and the form called from it will have this property?

Yes, that is exactly right.

Quote:

And does it matter what they default setting of oCaller is? 0, .F., ""?

I would set it to NULL, since that is the normal "empty" value for an object reference. But you can't set a propert to NULL in the class designer; you have to do it in code.

The default value applies if the form is the highest-level form, which in your case is the ribbon. So you could do something like this:

CODE -->

* In the Load event of your base form class
THISFORM.oCaller = _SCREEN.ActiveForm

* In the Load event of your ribbon form
THISFORM.oCaller = NULL 

In all your other forms, don't do anything special in the Load event. Just let it inherit from the base form class.

One problem is that, if you try to reference _SCREEN.ActiveForm when there is no active form on the screen (for example, if you were calling a form from a menu), it would raise an error ("ACTIVEFORM is not an object"). You can avoid that by doing something like this:

CODE -->

* In the Load event of your base form class
THISFORM.oCaller = IIF(_SCREEN.FormCount > 0, _SCREEN.ActiveForm, NULL) 

I haven't had time to read your subsequent question. It's possible that Olaf has answered it. I'll come back to it later.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads

RE: Controls "outside" the Form

(OP)
Hi Olaf,
I'm still missing something.
Here's what I have (simple example).

The Ribbon has the RTF controls in a container on Page 2 (that shouldn't matter). But as you know it's in the Top Level form.
My test form (RTFTest) is it's on form IN the Top Level Form, and has some RTF box on it.
The RTFTest form creates oCaller in the LOAD event of itself (THISFORM.oCaller = _SCREEN.ActiveForm) when it gets launched by the button on the ribbon bar.
The INIT of the Ribbon, I put in your suggestion: goAPP.oRibbon = THIS (so I assume makes an object called oRibbon that has all the properties of THIS (being the properties of pgrRibbonBar, and its objects).

So my dilemma then is, I select text in the OLE control on the RTFTest form.
When I go back to my RTF Control in the ribbon and I change the font size from 8 to 12 (as example) from my dropdown listbox for Font Size, it's Interactive Change event needs to tell the RTFTest OLEObject to apply the font size to the selected text.
I guess now I'm confused about the state of goAPP.oRibbon... When I click FONT SIZE change, how does oRibbon become aware of that change, and more importantly how does the OLEcontrol with my RTF text that is selected, know to apply the font size from the previous "10" to the now state changed "12"?

There is a big gap here I don't understand.
-S

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

1. so I assume makes an object called oRibbon
No, oRibbon has to preexist or you need a call of ADDPROPERTY. Setting a nonexisting property will give you an error you should have had that. Notice, this is advice of the more general type, not having any little tiny baby step you need.

2. You wanted the oRib bon reference, didn't you? I thought of it as the form reference, not as the pageframe or control reference, that would not be calld oRibbon, would it`?
Whatever you wanted to do with it, do it.

The hint on BINDEVENT is about a totally different solution strategy, this is about two separate solutions and you should notice that. Why would you need an oRibbon reference, if the child form/control can notice a goApp.iLangugeID or in your case a goApp.Fontsize property change itself?

The idea about status and information and binding to their change is more valuable in this kind of involvement with the ribbon. Neither the ribbon should need to know what it influences nor should a control on any form - child form or main form - know the ribbon structure to pick out an information. Both ribbon and form should act on the so-called model. Data, properties, state, information. That's what you share.

The necessity to know references comes, if you want to interact with more than just the necessary information.

So perhaps let's concentrate on the effect of binding to a property with a short example:

CODE

goApp = CREATEOBJECT("vfpApplication")

* event source: A textbox on screen:
_screen.AddObject("txtFontsize","Textbox")
WITH _screen.txtFontsize
    .controlsource = "goApp.nFontsize"
    .visible = .t.
ENDWITH

oEventsink = CREATEOBJECT("eventsink")

SET ESCAPE ON
ON ESCAPE CLEAR EVENTS
READ EVENTS


DEFINE CLASS vfpApplication as custom
   Appname= "My fine app"
   iLanguageID = 1 && english
   nFontsize = 10
   
   PROCEDURE Init()
      * Initialisaze anything, eg load config
   ENDPROC
   
   PROCEDURE QuitApp()
      QUIT
   ENDPROC
   
   *....
ENDDEFINE

DEFINE CLASS eventsink as custom
   PROCEDURE INIT()
      BINDEVENT(goApp,"nFontsize",This,"FontSizeChange",1+4)
   ENDPROC
   
   PROCEDURE FontSizeChange()
      MESSAGEBOX("Font size changed to "+TRANSFORM(goApp.nFontsize))
      * instead of displaying this new knowledge, this can apply it to itself, eg if eventsink is your RTF control
      * it can act on the current selection, of there is any, and change that selection font size.
      * Whatever you want.
   ENDPROC
ENDDEFINE 

Notice some the important characteristics of this: Both the "Ribbon" (here just represented with a textbox on the screen) and the form (here represented with the eventsink) don't know each other. The font size textbox (ribbon) just changes goApp.nFontsize and that in turn is noticed by the eventsink (which could be the form with RTF control or the RTF control itself, wherever you add the method triggered by the nFontSize property changing its value)

This is decoupling things. Now you neither need code in the ribbon to address rtfform.pageframe.pageX.rtfcontrol.Selection.Fontsize nor does the RTF control needs to know oRibbon.pagframe3.fontrelatedpage.txtFontsize.Value, where it finds the picked font size.

You may think it's getting more complicated if not two objects know each other, but interact via a third object, but it is important for the topic of encapsulation. This third information interchange object (goAPP and it's properties, you might vary this in many ways) is just a dumb data storing object. Each active object, the ribbon form and the RTF Control form, only knows itself and gets information from outside. You avoid the entanglement of knowing each other directly and the maintenance that would mean. If you'd ever change the location of the font size picking control in the ribbon, every single place in your whole application related to that would need to change.

Sharing object references instead of only sharing state and information should be limited to the cases you care about this type of entanglement and see both involved classes as belonging to each other like mother and child do. But in this case you have any child, any form of your application and the ribbon, the general toolbox of your application. The entanglement of knowing each other directly via references is a good case, if the both belong to a certain feature, eg an order list form and an order details form might perhaps know each other to implement some interaction with each other. Making any form know any other is like programming with GOTO, this is not advancing, what you could do, this is spaghetti programming. So Mikes idea would rather work for a situation a form you start from menu/ribbon calls a child form, so that child form is much more related to the parent form than to the ribbon form. Of course, every form has a relation to the ribbon form, as it has general options and settings, but in itself, it should only influence a settings object and any form, which wants to react to setting changes should do so via binding to the settings object, too.

And also think about it this way: It's much easier to define such setting properties and stay with them the next 1,2,5,10,20 years then defining forms and pages, etc. and stay with their structure to not need to adapt any code referencing each other. The moment a form accepts a reference and acts on it, you can see its definition including in at least the part of the referenced class you act with. This means a dependency, which you take as everlasting burden. Even though it is part of two separate class definitions.

Before you begin adding all kinds of properties to goApp directly look back how I talked about sub-objects, handlers, which are the first level objects of goApp. Eg the font size could really have its place in goApp.oSettings.oFontsettings.nFontSize and you could also differentiate a general application font size driven by config data and a current ribbon font size influencing only the current bound controls or their selection. That also means if you don't want a ribbon font size change to affect all controls having their own FontSizeChange method (which really becomes an Event by the bindevent binding), but only the active control. There is UNBINDEVENT() to unbind as soon, as a control is not the active one anymore and then BINDEVENT again when the control becomes active.

Bye, Olaf.

RE: Controls "outside" the Form

(OP)
Olaf,
Early in my startup.prg before the MAIN form is called and set with READ EVENTS, I issue:

goAPP = NewObject("cusAppClass", "AppClass.PRG")


I use a trick from Tore in that class with this function:

CODE

FUNCTION This_Access(tcMember)
	IF ! PEMSTATUS(This, tcMember, 5)
		This.AddProperty(tcMember, NULL)
	ENDIF
RETURN This
*
ENDFUNC 

So later at the init of the Ribbon, I issue:
goAPP.oRibbon = THIS

That is why there is no error.

I'm looking through the rest of your solution now to figure out how I put it together with my application. It is not so intuitive to me, so hopefully I can get it to work in a try or two from now.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

I edited my post and added some more explanation, so please read back.

Also, it was not meant to be integrated as is into yyour application but as a standalone sample. So you pick out, what you really need, of course you have your own READ EVENTS logic, etc.

Bye, Olaf.

RE: Controls "outside" the Form

(OP)
Hi Olaf,
Yeah, I figure the meat of your solution is:

CODE

DEFINE CLASS eventsink as custom
   PROCEDURE INIT()
      BINDEVENT(goApp,"nFontsize",This,"FontSizeChange",1+4)
   ENDPROC
   
   PROCEDURE FontSizeChange()
      MESSAGEBOX("Font size changed to "+TRANSFORM(goApp.nFontsize))
      * instead of displaying this new knowledge, this can apply it to itself, eg if eventsink is your RTF control
      * it can act on the current selection, of there is any, and change that selection font size.
      * Whatever you want.
   ENDPROC
ENDDEFINE 

And

CODE

* event source: A textbox on screen:
_screen.AddObject("txtFontsize","Textbox")
WITH _screen.txtFontsize
    .controlsource = "goApp.nFontsize"
    .visible = .t.
ENDWITH 

I have heard of BINDEVENTS, but I've never used it, so I'll need time to figure out how it works with the way you've put it together. The "objects" defined outside like goAPP and manipulation of them like oActiveForm are taking some getting used to.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
Ah, re-reading it now. I'm still having a lot of trouble, I think maybe because it isn't intuitive in how I think of how things "work". I see some new explanation, so I'll try again, but my first two attempts were failure.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
So question, in your code:

Is this:

CODE

* event source: A textbox on screen:
_screen.AddObject("txtFontsize","Textbox")
WITH _screen.txtFontsize
    .controlsource = "goApp.nFontsize"
    .visible = .t.
ENDWITH

oEventsink = CREATEOBJECT("eventsink")

SET ESCAPE ON
ON ESCAPE CLEAR EVENTS
READ EVENTS 

In the INIT of the Textbox on the form?

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

>The "objects" defined outside like goAPP and manipulation of them like oActiveForm are taking some getting used to.

Well, you can put the nFontsize anywhere, just both sides, the ribbon, and a form have to be able to address it as controlsource and/or as object/property to bind to.

If you make the font size a property of the ribbon, you have not made the problem to know the ribbon much easier. It could only be in a more stable and obvious place, at the root level of the ribbon form as form property.

You already decided to go for goApp, therefore it is your usual place for anything needing a scope you can't solve within one of two involved objects.

Wanting to have properties on the one or other side is like thinking of foreign keys only ever being part of the one or other table related. But in table relations you also quite often have the need for a third table to act as n:m relations, eg between for example persons and addresses.

I also already hinted on configuration, so there is a place for the default font size in a configuration record and then there is a config data loader involved, what else is best to handle access of more than one object than a totally separate object, just like a table is? Since triggering behavior of the front end with a change of data has limited justification in triggers or field rules, which only should be concerned with data validity and quality, you load this into a separate object, which you can bind to.

goApp or sub-objects of goApp cannot only bind font size to RTF controls, you have a n:m relation object with many fields relating many forms/controls to many ribbon features, font size is just one example.

Bye, Olaf.

RE: Controls "outside" the Form

(OP)
Once I see it working, I think it will help a lot.
You mention it works for RTF and any field, but the RTF every character could be different (I know it won't be, but it's not out of the realm of possibility to have 2 - 3 fonts and maybe 2 - 3 sizes for each font within a long RTF box). So that is dependent upon SelText or SelStart if I understand correctly. Maybe I'm thinking about it too hard, but I thought it would need to cover those cases, versus the whole text box being applied with one font and size.

I had major dental work today, and I'm still foggy from the medication... maybe I should resume this tomorrow.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

Yes, you can have many sections with different fonts and sizes in word too, and the word font related Ribbon control will reflect that two-way, depending on where you put your cursor. But it doesn't have to be this way. It surely is not the concern of the fontsize picker of the ribbon to know the current font size, this is with the RTF code. You don't have to be as interactive as Word documents are, also only set the font and font size of the active selection in the moment of the change of the font or font size. But if you want to, of course you could act on RTF control events to set goApp.nFontsize to the fontsize at cursor position and then you have that two-way, too.

You also have a wrong idea what I meant with different fields. I was talking about binding totally independent and separate features of the ribbon with different controls, you don't just have the ribbon page or section for fonts, do you? Each and every section and control on the ribbon should just influence a data object it is bound to and whatever forms or controls need that info subscribing to changes via BindEvent. They don't subscribe to ribbon control value property but to properties of the data object connecting this. Why did I talk about goApp.oSettings or goApp.oSetup so many times already, not only here? goAppp is the host of many things, including many features and their settings. Anything that is of interest in more than one or two objects has to go there or into any other third thing like database tables are. goApp is your place for many quasi-static things like the application name (if you ever think of changing it you only want it to be defined in one place, not in 100s of MessageBox() calls third parameter value, don't you?) and goApp is your place of current application state like active language and also hosts active components like a form handler, database handler...

Simply look back to all I suggested about goApp already in your thread about public variables. So far you just picked out a few things and you do so in a way, that will need refactoring later, as you don't go the full monty right away.

Bye, Olaf.


RE: Controls "outside" the Form

(OP)
Olaf,
Ok, I like this idea:

Quote:

The idea about status and information and binding to their change is more valuable in this kind of involvement with the ribbon. Neither the ribbon should need to know what it influences nor should a control on any form - child form or main form - know the ribbon structure to pick out an information. Both ribbon and form should act on the so-called model. Data, properties, state, information. That's what you share.

The necessity to know references comes, if you want to interact with more than just the necessary information.

So if I understand it correctly, there is an object "outside" two other objects that is acting as a mediator. In this case, let's call it goAPP.oRibbon which was created in the pageframe init with goAPP.Ribbon = This (and I'm not clear here, should I have the whole form as a reference, instead making goAPP.oRibbon in the Form's Init?)

So now my goAPP.oRibbon has an object reference to the pageframe/form (depending on where created), that has pointers to all the same property and method names in the Ribbon?

Is this correct so far?

I look at it in the debugger, and I can see the object goAPP.oRibbon and it looks like it's the pageframe and all its contents, which for this purpose is fine, but I think you're suggesting if I put it at the form INIT instead, then I'll have access to all the objects at MAIN form, which has other things besides the ribbon I may want access to? Is that right?



Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
Ok, so now I progresses:

I look at it in the debugger, and I can see the object goAPP.oRibbon and it looks like it's the pageframe and all its contents, which for this purpose is fine, but I think you're suggesting if I put it at the form INIT instead, then I'll have access to all the objects at MAIN form, which has other things besides the ribbon I may want access to? Is that right?

So in the ribbon where the fontsize listbox is, I added in its Init:

CODE

_screen.AddObject("txtFontsize","Textbox")
WITH _screen.txtFontsize
    .controlsource = "goApp.nFontsize"
    .visible = .t.
ENDWITH 

Since "EventSink" you say represents the form, then I assume I need to put the:

BINDEVENT(goApp,"nFontsize",This,"FontSizeChange",1+4)

In the form that's using the RTFedit box INIT?

So now I added the method FontSizeChange to RTF control.

This.SelFontSize = goAPP.nFontSize

Ah... note this is edited: It's working. I changed the _screen.txtFontsize as your example (I see that now) to WITH This ... ENDWITH within it's init code, and it worked.

Now, the one oddity I have, would be helpful is, the selected text in my RTF control in the form as soon as I go to the Ribbon and click the text size drop down, the selected text reverts to non-selected. I change the size, then the text that was selected changes its size, when I click back on the child form. the text is selected again. Is there some way to keep the selected text showing throughout? It's just a little "glitchy" behaviour compared to other editing interfaces. I tired setting ThisForm.LockScreen = .T. in the Form's LostFocus and back to .F. in GotFocus, but this didn't change the behavior.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
Just when I thought I had it...

I was tempted to start another thread, because this one is so long, and this issue is related, but kind of separate.

So after I got the fontsize change to work, I got all ambitious and thought I'd add fontname next.

So what I did was in the INIT of the RTF control Added in a second BINDEVENTS:

BINDEVENT(goApp,"nFontSize",This,"FontStateChange",1+4)
BINDEVENT(goAPP,"cFontName",This,"FontStateChange",1+4)

I decided to make a single method in the OLERTFcontrol to "FontStateChange" since there are a bunch of attributes I'll be tracking, and maybe more than one change at a time, so instead of a string of calls to different functions, though I'd just apply all the changes at once with:

WITH This
.SelFontSize = goAPP.nFontSize
.SelFontName = goAPP.cFontName
ENDWITH

The problem though is, now, as soon as I try to select any text in the OLE control, it blows up.
With Error # 1429
Error Message:
OLE IDispatch exception code 0 from RichTextControl: Invalid Property Value

Now, I commented each out (so just font or just size) was enabled, and singularly they work... so can you not have more than one BINDEVENT in a control? Or is there some other way I should do this? Am I confusing the OLE control with too many parameters?
*
Or do I have to keep each one in its own method?

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
Well, lesson learned.
You have to make them separate, which is fine. Thought combining them would be elegant, but the OLE control doesn't like it. :)

So that's solved, sorry for the rambling.

Though my issue with selection not showing while away from the form remains, so if anyone has a solution to that, I would be really grateful.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

(OP)
BTW Olaf, I'm just starting to scratch the surface on understanding how this actually works, (but it was still easier than images in a grid! :)
Thanks for taking me through this. I have to say the whole thing is very non-intuitive, I don't think in 100 years I'd have ever figured this out on my own.
Greatly appreciate it.
Now this is something that suddenly makes our application extremely modern and vibrant. I can't wait to apply it to all of our edit regions, and then start to work with printing the RTFs that Mike G and others have mentioned.
In my industry, this is going to be such a competitive advantage.
Many thanks.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

Let me try to catch up.

>So if I understand it correctly, there is an object "outside" two other objects that is acting as a mediator. In this case, let's call it goAPP.oRibbon which was created in the pageframe init with goAPP.Ribbon = This

No, this outside mediator object is goApp, or goApp.oSettings or other sub-objects. It is NOT goApp.oRibbon. That was the solution you wanted, but that is making the ribbon form a known reference to everything and that will mean, that a control needing an information now knows a reference to the ribbon, but will need to "know the ribbon structure to pick out an information". And that is unfortunate. When you want to allow customers to reorganize the ribbon in any way or when you reorganize your ribbon years from now, then all code accessing ribbon objects will need to change.

This idea you only got half is just like the normal sharing of data. You have forms bound to tables, if you need access to customers, you address the customer table and not the controls of the customer form bound to these tables, don't you? The goApp.oSettings will not be a reference to a table, but to objects and properties having loaded config data or default values or just the current values set by ribbon controls. But what is important is, the structure of the oSettings is merely giving the settings a reasonable name that can stay this way forever, no matter how the ribbon changes.

This is also not the first time I say this. What are you doing? Are you skipping each second sentence, are you speed reading this and only picking up 50% of it?

Further answers separately. Later.

Bye, Olaf.

RE: Controls "outside" the Form

(OP)
Hi Olaf,
Ah, I see yes. The goAPP already existed, and I created it very high in the process before any forms exist. Then we make a reference to some other existing object, in this case the ribbon pageframe (Though it seems it would be better if I reference the whole form instead? I'm not sure why, but when I get bit by it some time in the future it will make me go "OH, THAT's what Olaf ment".
As for other stuff, not speed reading, but maybe not far off in that I'm comprehending about 1/2 what you are saying hence the feeling of 50%, and still very fuzzy on some other areas. I'm sure it is obvious to you, but for me, for some reason, some mental block, and I only understand once I can explain it back, and I'm not there yet (which is why I was trying to explain it in my post... if I say it out loud, maybe it makes sense to me... I have a very weird learning method, but I'm stuck with it).
I would say just know I do understand about 1/2 of what's happening. On the plus side, once I get a working example of something, I tend to be very quick to replicate it. I had some stumbling block initially with some of my other controls because they are (oddly) simpler than the drop down boxes. They don't need control source, so I wasn't sure how to manage it, then I just assign the goAPP.<value> = <some value> directly, and the BINDEVENTS took care of it (like Font Color, I have button that is just like Word color button... when you pick a color, it changes the bar under the A in the button to that color, and that color is passed to the OLE.SelFontColor via goAPP.nFontColor property... That was COOL.)
So I think I have the way now for the ribbon to talk to the control. Since this is all about RTF editing, I don't really see this part change in the future. OLE box won't change, Font picking and attribute setting won't change... if it does, the OLE control can't manage it anyway (like superscript and subscript... can't do those already). So I'm not too worried in this case about the years from now change of this, though I get I should be worried about that overall for something more dynamic.
At least for now it is working, and I can mature it later, as I come to terms with understanding WHY and HOW it works, than I am able to grasp for now.
I'm happy being the C student.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

>OH, THAT's what Olaf ment

I switch from suggestion to reacting to your rejections, that's why there are multiple ideas. So you have separate the ideal suggestions I made from reactions to your rejections of ideas and to make compromises. Having goApp.oRibbon at all is such a compromise. In my ideal practice suggestions it will not exist. The ribbon should be self-contained and encapsulated in itself, forms should be self-contained and encapsulated each in itself. Their interaction and share of information is via goApp.oSettings, or whatever else you imagine a better name for an information interchange object. A third object.

Bye, Olaf.

RE: Controls "outside" the Form

(OP)
Hi Olaf,
I guess I am not understanding. Where would goAPP.oSetting be established?
Since it's just a reference, isn't oRibbon really irrelevant, it's just a name, though it gets assigned at INIT of the Pageframe, can move that to the form or before the form right?

I like this idea actually, but I thought it wouldn't work for my case for some reason. I think you think my grasp of OO is better than it is. I wish that were so, and I'm getting better but some of this is like speaking Japanese...
My analogy here is this. I live in Japan. If I say something in Japanese to someone like "How much is that" (Kode wa ikurda deska), they suddently think I'm fluent in Japanese... so when they answer me back, in something other then exactly the cost of what ever I pointed at, I have NO idea what they are saying.
I think our communication is sometimes like that. I appreciate that you think I understand this, but really... I don't. Parts I get very solid. The DB side, NO problem (how often you see me ask a question about database manipulation?) The OOP and interface side, very complicated for me. Still trying to get my head around it, and worse after having been "off" for so many years. And so many things I was "told" were the way to do it back in VFP 3, 5 and 6, now everyone says "don't do it that way..." It's very confusing to me sometimes. I know that the only reason I get anywhere in this stuff is just pure tenacity. I keep grinding away until I find a solution. I'm sure sometimes my solution isn't the best one. So I always try to make it better later, if for now, it's the best I can do.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

RE: Controls "outside" the Form

The idea is, that you don't act on ribbon controls from outside the ribbon, but act on the pure informations in the form of object properties of oSettings. This is the whole nature of the suggestion, to AVOID acting on ribbon controls. oSettings is the third object aside of the ribbon form/controls and the child forms the tools should influence. The interacton goes through oSettings via diferent bindings. The ribbons binds to oSettings properties as controlsource of its tools, the forms bind to their changes via bindevent.

Bye, Olaf.

RE: Controls "outside" the Form

(OP)
Well, I'm interested in doing it right. I'm sorry I deviated the conversation, I thought it was going the preferred method.
So if I create goAPP.oSettings at the startup.prg is that the suggested approach?

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."hammer

Red Flag This Post

Please let us know here why this post is inappropriate. Reasons such as off-topic, duplicates, flames, illegal, vulgar, or students posting their homework.

Red Flag Submitted

Thank you for helping keep Tek-Tips Forums free from inappropriate posts.
The Tek-Tips staff will check this out and take appropriate action.

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members!

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close