Create Customizer with Property dropdown

I am creating a custom vision component that pushes values into the properties of other vision components on the window. Normally to do this, I would have to set up an expression on the downstream component property to accept a value from one tag or the other, depending on a specific case. For instance, on the value of a numeric label:

if (component1.enabled, {someTagName}, {otherTagName})

I want to build a customizer that looks like this:

TagPath Property
[default]someTagName component1.value

With the tag browser tree and property browser tree on the side. Then, when the 'enabled' property of the custom component is activated, push the values from the TagPath column into it's respective property via the Property Binding system. I recognize that this is something that has probably never been done before and relies on the fragile Property Binding system. However, I consider myself a pioneer and would love to attempt.

All this being said, is this something that can be done? If so, what is a good starting point and what should I watch out for? I am specifically stuck on setting up the Property Tree browser and creating a good binding.

Bindings are designed to pull values into target properties, not push them. I think your approach is fundamentally broken. Even if you get something working, it will be utterly unintuitive to the poor designers who follow you.

Perhaps you should share more about your motivation for this approach.

I phrased it poorly. You could say that I want the properties designated in the Property column to 'pull' values from the tag paths found in the TagPath column. My motivation is to minimize the workflow of setting up my custom component, as it will most likely be built into all the templates my company has developed thus far. And that alone is no small feat. I was thinking I could get something rolling with the AbstractPropertyEditorPanel class, but unsure how to implement.

That's still "pushing" into the target properties. If you absolutely need to force tag subscriptions like this (in an SDK component), you should do them outside the binding system.

I would recommend against it entirely. You are setting yourself up for a maintenance nightmare.

What do you mean outside the binding system? Do you mean by simply 'writing' a subscripted tag value into the property? What is the syntax for that? I am not bedded with the idea of using the binding system (which again, is fragile).

Yes, you set up a tag subscription listener on component startup, writing directly to targets as values arrive, and unsubscribe on component shutdown.

But again, I strongly recommend you not do this. Use ordinary bindings to supply values to your components.

And if my target is a property on another component, how do I reliably allow the user to select that property from a tree (like in the expression property window)?

Don't. Let the user be responsible for placing a binding on that other component.

Really.

Values arriving in a component's properties from an unknown source is an utter disaster for future colleagues. Don't do it.

2 Likes

As I said, I am attempting to pioneer a new workflow for my company. I am not trying to supplant IA's initial design decisions, but augment them. That being said, is there a way to write to the property of another component?

Sure. Call the appropriate setter method on the target component.

Could I potentially use the PropertyChangeSupport class to aide in mapping tag paths to specific properties?

No, because that is optional and is typically only used by components that are a composite of inner components. (It is only applicable to components that will receive values, not send them. That is, pull, not push.)

1 Like

I guess I should provide a little more context. My custom component is essentially going to act as a tag value simulator, pushing values from a correspondent tagpath in a table into the property of another component. This means that the standard tag subscription and subsequent property setter method won't work.

History playback system?

If the target properties would normally be bound to an actual tag, consider using ClientTagManager.addClientTagProvider() to inject a proxy for gateway tag providers as desired.

The proxy would have to intelligently forward any non-relevant requests to the real gateway tag provider, and wrap any subscriptions for later redirection.

So if I have a gateway tag provider named 'default' and I activate the tag injection on my custom component, I would then spoof a client tag provider (also named 'default') with copies of the exact same tags, feeding the values I have pre-created? Sounds like a good option, but I have no idea where to start. How would I set the ClientTagProvider in such a way that it takes over feeding values to the target component?

Yes. But I've never done this. I just verified in the javadocs that this can work. You will have to blaze the trail.

Also note that there's no .removeClientTagProvider() method. You will have to keep the shim in place even when not performing substitutions.

I will attempt. As long as I can 'deactivate' the ClientTagProvider when needed, this is a great solution.

I am having trouble retrieving the target component by name from inside my custom component. Could you point me in the right direction? This is what I have so far, but feels very clunky:

public VisionContainer getPMIRootContainer() {
        Component current = this;
        while (current.getParent() != null) {
            current = current.getParent();
        }
        if (current instanceof VisionContainer) {
            return (VisionContainer) current;
        }
        return null;
    }

    public Component getComponentFromRootByName(String name) {
        VisionContainer root = getPMIRootContainer();
        if (root != null) {
            Component component = root.getComponent(name);  // Lookup by name
            if (component != null) {
                return component;
            }
        }
        return null;
    }

Could do something like this, to access via the same pathing we use through Ignition:

    public static Component getComponentByPath(Component component, String path) {
        BindingRoot root = IgnitionSwingUtilities.getAncestorOfClass(BindingRoot.class, component);
        if (root != null) {
            return root.getComponentForPath(path);
        } else {
            return null;
        }
    }

(post deleted by author)