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!

Building Ribbon Functionality in Application 2

Status
Not open for further replies.

Scott24x7

Programmer
Jul 12, 2001
2,826
JP
Hi All (And Mike),
I decided to start a new thread since the other was about a pageframe property, this is gone down the path of really creating based on a different idea, which splintered from that discussion.

So I have encountered one weird issue. Originally I put a container with just a label in it, but I want to "mimic" the current Office style ribbon bar. (I find it does look quite good). And the way they "highlight" the active tab is by drawing a sort of 3 line frame around it (left, top, right). I initially thought I would just turn the border property on the container to width = 1, and when I lost focus to width = 0, but when I leave the object, (even when lost focus calls for a refresh) the container keeps it's boarder. Am I doing this the wrong way?

I tried a second method, putting an actual box in the container, and a "line" at the bottom to mimic the state of the bottom of the tab, where I set the color property either equal to the form background property (when the container/tab is selected), and to the border color of the pageframe when it's not selected. Then set the visible property of the box to either .T. or .F. depending on got or lost focus, but the box persists once the container has been clicked on.

It seems the refresh clause shouldn't need to be called when the lostfocus has the visibility property changing from True to False...

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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Ok, so strangely my "lostfocus" event is never firing. Is there something "magic" about container's I'm not aware of?
I click on the object, even set a "gotfocus()" call from the click event. When I click on another button, it just goes to that button, and the code in the lost focus of the container never fires...

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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
The LostFocus event for a container has the same nature as for the form:

help said:
A form loses the focus when the form has no controls, all its controls have their Enabled and Visible properties set to false (.F.), or another form gets the focus.
Just put in container instead of form.

You will not want to control this with focus, even when the tab you emulate has focus for the moment the user activates the page, the next step will put the focus on some control within the page. So you want something that is not at all controlled by the focus. You want something that changes the active tab look as long as another tab becomes active. Focus is the totally wrong concept in this context, as the focus is always just on one single control, it is not at the same time on parents, they only are active, but focus is really only a very atomic thing.

Bye, Olaf.

 
This is a classical issue of first setting all instances to some defaults and then only set the one currently activated instance different.

Here's how I would go about this: When you have a series of self-made tabs on your form, then let's assume they all are based on a class tabcontainer. You design it with the look for an inactive tab, on click you change its look to activated by setting some parts visible or invisible or change color or width. You do that after you reset all such tabs to their default look using SetAll("prpoerty",value,"tabcontainer") so theis SetAll influces all tabcontainers. Or use the objects ResetToDefault(). You can combine this by using one triggering property and define a property_assign method, which does This.ResetToDefault(...) on some if its properties.

Bye, Olaf.
 
I was about to say that getting or losing focus is not relevant here; you need to be concerned with focusing of the controls on the page, not of the device that selects the page. But I see that Olaf has already made that point.

You mentioned the "refresh clause". If you mean the Refresh method, that is not relevant either. Refreshing a control serves no purpose unles the control is bound to data, which is not the case here. (Or did you mean something else by "refresh clause"?)

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Wow, feeling particularly stupid at the moment. I had always thought the Refresh() clause was related to all elements of the object, so if a color change, you needed to do a refresh of that object for it to be reflected on the form. Didn't realize until now it's only related to data... There are a bunch of refresh calls I can get rid of... (And I'm going to go do a global search for it across my forms...)

That said, I'm more confused now. It must mean that calls to things like This.BoarderWidth = 0 are not getting executed. That could be because I have them in the LostFocus method. (Sorry Mike I said "Refresh clause" earlier, and I did mean the Refresh method, but bu "claus" I mean the actual snippet in the method. "Clause" is a carry over from other programming language days).

I also thought by calling the refresh() method, any other changes I had would be affected there, so by calling the refresh method directly with a This.BoarderWidth = 0 would also be executed. But the "Lost Focus" event isn't happening when the container loses the focus... for reasons Olaf described (which I find a weird way to describe "lost focus", compared to all other objects, except forms, as he's also pointed out.

Let me tinker with this newfound understanding, and see if I can make it work. I also didn't realize there was a pagetab object. Another "Mr. Obvious", as Olaf mentioned it, a little bell went off in my head, so I may instead, tinker with that object too.

Thanks for all the good insights lads.


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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Scott, your new understanding is correct. As you have now realised, you do not need to call the Refresh method to give effect to things like colours and borders. In fact, there is a penalty in doing so, in that if the control happens to be bound to data, then it takes time for VFP to get that data in order to refresh the control.

Regarding the "pagetab" object - actually, there is no such thing. There is a page object, which displays many of the characterstics of of the actual tab. For example, if you set the page's BackColor to red, then the tab will appear red (sorry if I'm stating the obvious). But the tab doesn't exist as a separate object.

Mike



__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Tom,
I've decided to do it from scratch. I looked at Emerson's Themed Controls, but that one has two problems: 1) He's not finished the ribbon, and more importantly 2) it's really based on the look and feel of the Office 2007/WinXP style ribbon which is a bit garish in my view. I tried to get it to run, and it had all kinds of control issues as well, so with the level of complexity of the incomplete item, I decided to pass.

Guillermo's FoxRibbon appearance is also based on the 2007/WinXP look and feel. Maybe that can be tailored/toned down, but I also like the notion of my approach being much simpler, and some things I like to *know* the functionality of it, rather than accepting a black box.

The funny little stumble I had above with the controls is now solved with the simple understanding of how container applies Got/Lost focus. That's now completely resolved, and the feel of the bar is just like that of current Office versions. I've embedded a screen shot of the bar here:

RibbonBar_ur0di9.jpg


As you can see, the outline of the tab selected is just like what it looks like in modern office. You can see also, the "Configuration" menu is selected, and the corresponding tab is showing "Configuration". So with all this behaviour now working, I can start to populate the individual pages in the frame (which is set to width of screen at start, and resized if the size changes), with controls to do what I wish. I also will add a "Expand/Contract" button at the end of the page so the ribbon can be opened/closed, which I plan to do by simply setting the size of the height to 0 and move open windows relative to their position in the screen when it expands or contracts.

I think I'm 75% of the way done now, and that seems to have been the hardest part. The rest now is just like manipulating a pageframe and use it's objects to control everything else.

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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Hi Mike,
Thanks, I was aware of the page properties, and I have since found I misunderstood Olaf's comment on that, as I couldn't find a tab class when I went to make one.
BUT, funny enough, I realized that my "usage" of "Refresh" wasn't entirely wrong either... and if I make a call to it, it does what I tell it to do. The problem I had was calling it from the lostfocus method which was never actually getting fired, for reasons I now understand. What I did find is that "CLICK" event is your friend. So what I do on each of the buttons now, is call all their Refresh() methods, where I put the code to get the behavior I want. I supposed I could move all that to every button's click event, and then call all the buttons click events when one is clicked, but I "LOVE" the obvious meaning of "Refresh". Your point about data being bound is an interesting one, but does it really slow it down if there isn't anything to call? If that's the case, what I may do instead is create a custom method called "FakeTabRefresh" or something like that that I call instead of the native Refresh, and then I still get the benefit of having some reminder in the method of what is going on. That's why I didn't want to use "click" because it feels obscure, and not very informative of what is actually going on when you're hopping across programmatically "clicking" everything.


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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
I was referring to your pagetab class, as you said you made your own tabs on a pageframe with tabs=.f.
I assumed you defined a class for that. Are you really putting containers with labels on the form and program in them on the form itself? Make use of OOP.

Bye, Olaf.
 
Hi Olaf,
I have a set of buttons what are a sub-class, and all of which use OOP and inheritance to function. Since the Pageframe without tabs exists then you just need some way to tell the button (which is no unique to the ribbon, but I use in many other places), one with text one with image, but they are both based on the same sub-class "Modern Button" which is a container with 1 object in it. In the case of the tabs, I added one object to the container, a "line" so that when the button is selected, it's bottom border can be over-ridden with the line, and set color properties to what you wish. It's elegant, simple and I just had to work out the "refresh" behaviors, now it all works like a charm. All the buttons really do is create an appearance (behavior as seen in the image above), and then setting the page that they are related to. Adding more is easy now, I can even subclass these as "TabButton" so they are easy to apply. The leading button is a little bit different, but it essentially will call an overlaying modal form when it's selected, which is same behavior as Office.
This approach is much simpler than all the others I've seen, and I don't see the point of making them overly complex to the extent that they don't work in other settings. What I'm planning to do now is actually subclass the "Main" form so any new application can use this, and you just have to change the elements in the default tabs, or get rid of them if they aren't needed. The Ribbon "lives" outside the base forms anyway, and it is essentially a replacement for MENU.MPR. What I have now, is exactly what I wanted. And from what I've seen a much cleaner and simpler way of implementing such a thing that I can make look native compared to the other solutions. Simple is good...

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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
So after a couple of days, here's where I am with the application. A few quirks to work out as I've migrated to this philosophy, but this is what it looks like:

DCideMainScreen_gdlqul.jpg


The only annoyance is the large white space at the top of the form, which I still can't get rid of, which is also a pity because if I enter a caption, then it shows in that bar as well, but without one, the system tray just has the application icon in the tray with a blank name. Small issues, but for the look and feel, I'm pretty stoked.

Thanks to all who have helped with this so far.


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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
When I set Titlebar=0 in form.init() the white bar at the top is thinner than in your screenshot. It's perhaps just 3 pixel. Your whit spacing on top looks like a full titlebar height. You don't seem to do what I said.

Bye, Olaf.
 
Hi Olaf,
I have it set in the form property Titlebar = 0 - Off.

Also init has the following:
Code:
WITH _SCREEN
	.LOCKSCREEN = .T.
	.Width = 2040
	.Height = 1400
	.CAPTION = ""
	.TITLEBAR = 0
	.LOCKSCREEN = .F.
ENDWITH

So I agree, it feels like a full height bar, but I am not sure what is "overriding" it.


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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Well, I said to do it in init and you do. But why on earth are you also setting it at design time? This is making the titlebar NOT disappear and I told you.

In thread184-1777771 I said:
myself said:
it makes a difference if you try to set Titlebar=0 at design time already, or at runtime. You can set Titlebar=0 in Init and what remains in Windows 10 is a white line a few pixels thick.

This means I said if you set Titlebar=0 at designtime already, you kill the effect of removing the titlebar. Could you please read what's been told to you more thoroughly and pay attention? You seem to always want to do things differently as has been told to you, is that some kind of sport for you?

If I make a test form class with what you do and have both Titlebar=0 at designtime and This.Titlbear=0 in Init(), the titlebar does NOT disappear. This is why your form has the double titlebar, the native one and your own.

All that said it surely deserves a comment about the difference of only setting the property in the class and not in the Init() will NOT work and thus this has to be the way it is and you're not tempted to set the property and remove the code. I for example typically prefer to avoid such code and set properties at designtime. You always have to comment such things, or you keep shooting yourself into your own foot, as you do ever so often.

The reasoning, why it works the one and not the other way can only be accepted - seeing is believing, we don't know what happens behind the scenes. I assume the top-level type overrides the design time setting of no titlebar, so you have to switch this at runtime. And this switch at runtime seems only to have an effect, if it is 1 before, because, well, Windows obviously regards changing from 0 to 0 is no change and means nothing to do. Assumptions. I can't prove them, but the effect is visible and so we don't need to care for proofs, the evidence is visible. Just note one special thing about the titlebar property is its valid values are 0 and 1, not .F. and .T., that hints on it being a property of the Windows C++ TForm base class, that is the basis for the native VFP form class and it's not a VFP property, that would be designed with VFPs values for true and false.

Now I hope this was a detailed enough description, so it gets over.

Bye, Olaf.
 
Ah, I see. Sorry, that subtlety escaped me.
And yeah, this is exactly the kind of "VFP quirk" that makes it so loveable. :)

Thanks, I'm sorry I just missed that comment, and yeah, now it looks just spectacular.
I also discovered some things along the way about OBJTOCLIENT which became very useful in that drop-down scenario with my "flags" on the map. What a handy function! Never new it existed until now, but wow, it is crazy useful when having child forms on top level form, and forms get moved around by users (which happens all the time).

One other odd thing, you see I have that height property at 1400, and width at 2040... the width is right at run time, but the height takes the full top to bottom of screen. Any idea why that would be happening? That code is in the INIT of the Top Level form.


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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
I don't know your screen resolution, is it higher or lower than 1400 pixels height? I also don't know what you expected, how should I judge your situation, you only tell the half.

If you want fullscreen then I'd use WindowState and if you'd like to make it fullscreen without really being fullscreen, then SYSMETRIC() give you the measurements you need, including size of the taskbar to take into account eventually. Some API also helps finding out where the taskbar is, and whether it is visible. By the way Foxpro only offers a subset of what you may get from Windows API GetSystemMetrics function.

Bye, Olaf.
 
Hi Olaf,
We're using huge screens here, so we DON'T want the top level form to take up the whole space. We just want it to run in about 1600 high x around 2048 wide. (We us 43" 4K monitors, so we've got lots of screen space). So I find it odd that the _SCREEN.WIDTH works fine, but the _SCREEN.HEIGHT is ignored.


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

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Again, _SCREEN is the name of VFPs main form, this is NOT display size, _SCREEN is just a form, not a system monitor/desktop object. You've been told to use Sysmetrics(), because _SCREEN only tells you the desktop dimensions if _SCREEN is a maximized form AND if no taskbar is shown.

Also, you didn't answer my question about your display size. Inches don't matter if we talk about pixels. What's the display resolution?

2048 pixels width would suggest 1536 pixels height in 4:3 aspect ratio, but today's displays will rather have 16:9 aspect ratio, which for 2048 pixels width means 1152 pixels height and then 1400 obviously is too high.

Is it so hard to determine the real resolution of your monitor and program accordingly?

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top