Any way to enable/disable tooltips?

There are some practical problems with the dictionary method. One is that .Controls does not exist for the App, only Windows so it has to be done for each window. App.Windows is iterable, so you can iterate through each open window then each control, but that leaves open the possibility that a window could be opened, have to recache. Also a newly opened window might have the same control names. Can’t use a dictionary as a window property because it does not exist until opened. In opening you can cache it, etc. I’ve kinda made it work but there is more code in more places than it originally appeared.

Why would you need a case for each control class? Add an interface to handle it. Either way sounds like about the same amount of code.

You’d probably put the code into a module to make the storage global. And yes, there are some wrinkles that need to be worked out. But it’s probably the most general approach. Subclassing is not a bad idea, but it quickly becomes a big headache if you are already subclassing your controls, which is very common.

Or, you could invert the responsibility and use Extends to add the necessary functionality to each control. This is just a refactor of the approach I sketched above, and you’d still need some global storage and a system of iterating over the controls. But it might make the code more legible.

I don’t see how an interface applies here, if we are talking about native controls. You can’t impose an interface onto an object class.

You yourself said that subclassing controls is very common. I would never use a generic control. You always want to extend the base control in some way. This is a perfect example.

The problem I ran into with subclassing is (for example) while you can have the MouseEnter event in the subclass definition, it prevents you from using MouseEnter otherwise and that becomes a special case.

These are the reasons why it would be so much easier to do in the IDE.

Right click the Event in your SubClass and press this

image

Then at the end of your Event code in your SubClass, type RaiseEvent MouseEnter

You can now use MouseEnter as you used to use it on the Window when you add your SubClassed Button as it will run the code in your SubClass MouseEnter first then the RaiseEvent will call the code that you might want to place on the button event on the window (which you can now add).

Interesting, doesn’t look like that in the Mac IDE. I can create event definition from event with a menu choice, then I get a MouseEnter under Event Definitions separate from Event Handlers. From there it gets opaque and the documentation does not make it clear to me. No code is allowed in MouseEnter under Event Definitions. Where does RaiseEvent MouseEnter go? at the end of the subclass Event MouseEnter? Or in the instance?

Hang on here. I’m looking at this and it looks dead simple to me. Correct me if I’m wrong:

Make a module, let’s name it TooltipHelper.
Add a private property

Private Tips as Dictionary

Add an extension method:

Global sub TooltipsEnabled (extends w as Window, assigns value as Boolean)
    If value and Tips = Nil then Return
    For each ctl as desktopControl in w.controls
        If ctl isa DesktopUIControl then
            Dim key as string = w.name + ctl.name
            If value then
                DesktopUIControl(ctl).tooltip = tips.value(key)
            Else
                Tips.value(key) = desktopUIControl(ctl).tooltip 
                DesktopUIControl(ctl).tooltip = ""
            End if
        End if
    Next
End Sub

Then you define them in the IDE and turn them on/off by calling

Window1.TooltipsEnabled = True // or False

You can add an Event Handler and an Event Definition. Your code goes in the Event Handler, which can call the Event Definition via RaiseEvent. You don’t put code in the Event Definition. That code goes in either a subclass or in an instance you drag out on the window.

There is a fundamental difference between class methods and events.

With methods, the call sequence is bottom up. The first subclass that implements the method is the one that is called. It can cascade upward by calling Super.MyMethod.

Events are called top down. The most super class event is called. It can cascade downward by re-raising the event. Both allow you do to some setup code, call the method/event and then do some more stuff.

When you say “Your code goes in the Event Handler”, which one do you mean? The one in the subclass under Events or the one in the instance? When you say “call the Event Definition via RaiseEvent” what is happening there, if the Event Definition has no code?

I think you are saying that there can be code in the instance MouseEnter and code in the subclass Events MouseEnter, provided there is an Event Definitions of MouseEnter which prevents the compiler error that will otherwise result. Then the statement RaiseEvent in the instance MouseEnter causes the code in the subclass Events MouseEnter to execute. Have I got that right?

Ok that is clearer to me.

Event Definition is just a stub that says any subclass or instance of this class is allowed to make an Event Handler for this event.

Who owns the private property? the window? so you would need to do this per window, or iterate through each window? I guess if it is a window extension, then the app can have a public property which can be used to set the TooltipsEnabled Boolean whenever the window is opened?

Yes. But if you are already subclassing your controls - for example, an EmailEntryField that restricts input to valid email addresses - then you might actually have more work to do than if you iterated over the controls.

The OO argument I might also make would be to say that keeping the tooltip functionality all in one place (a module) improves maintainability and the ability to understand the code. Plus, as someone else noted, it makes the code portable and project-agnostic.

Putting the code in a subclass is encapsulation. Putting it in a method is not. Putting the code in the subclass allows each control type to respond in a different manner. Using an Interface allows you to treat different controls as if they were the same type. They are the type of the Interface. That allows you to iterate through the controls and call a single method on each of them, without needing to know the control type or the specifics of the implementation.

The module where the extension method is. The whole reason for the key to be the name of the window + name of the control is so it’ll be unique per window/control combo.

It could be easily updated to accommodate containers if necessary.

I was able to get this strategy to work, though not without issues. One is that there is no .name property for a Window, but using Introspection you can get the FullName which is unique so the control.name isn’t necessary.

However the module method would not execute unless I used the name of the window, Window1:

TooltipsEnabled (extends w as Window1, assigns value as Boolean)

Also the Property Tips had to be instantiated in the Window1 Open event or it did not exist.

So it seems would need to be duplicated for each Window instance? In that case it would be better to subclass Window to include the property and save/restore method? That is much better than subclassing all the controls, anyway.

I appreciate the dialog over this, I am learning a lot that might have nothing to do with this problem as well.

Something’s not right there. I’ll take a look at this after I’ve had some food.