Toggle and GameObject Active

I am trying to set up a view-delegate or Model-View-Controller pattern. I have several UI Toggle items all displaying and controlling a single GameObject property (imagine it is the “activeSelf” property). Click any toggle, and the property should be updated, If the value is new then the GameObject should trigger an event that causes all the Toggles to be updated to match the new value. Likewise, if a script sets the GameObject property, the event should again get triggered and the UI Toggle items get updated. Typically the property “set” mechanism should check if the new value matches the existing one and not trigger the event so as to break the callback cycle.

What is the appropriate C# / Unity mechanism / syntax for this? I can imagine the GameObject might need a “Observable” Component like this:

using UnityEngine;
using System;

public class Observable : MonoBehaviour {
    public event EventHandler ActiveStateChanged;
    private void OnEnable() {
        if (ActiveStateChanged != null)
            ActiveStateChanged(gameObject, true);
    }
    private void OnDisable() {
        if (ActiveStateChanged != null)
            ActiveStateChanged(gameObject, false);
    }
}

How would one set the rest of this up using C# events, delegates, etc?

With OnEnable and OnDisable, you don’t have to check if it’s already on or off, but if it was some other value, you might have to.
Beyond that, what is it you’re asking for “setting up the rest”?

There’s looooooooads of ways to do that. I’ve given two examples. One with a string, one with a active/inactive state for a GameObject. I mean you can use UnityEvents instead, or whatever, but I’m assuming it’d be a setup similar to this:

    public class ExampleModel : MonoBehaviour
    {
        public event Action<string> UpdatedExampleStatus;

        [SerializeField] private string exampleStatus = string.Empty;
       
        public string ExampleStatus
        {
            get
            {
                return exampleStatus;
            }
        }

        public void ChangeExampleStatus(string newStatus)
        {
            if(newStatus != exampleStatus)
            {
                exampleStatus = newStatus;

                if(UpdatedExampleStatus != null)
                {
                    UpdatedExampleStatus.Invoke(exampleStatus);
                }
            }
        }
    }
   
    public class ExampleView : MonoBehaviour
    {
        [SerializeField] private ExampleModel example;
        [SerializeField] private Text exampleTextView;

        public string AnExampleText
        {
            get
            {
                return exampleTextView.text;
            }
            set
            {
                exampleTextView.text = value;
            }
        }

        private void Awake()
        {
            if(example != null)
            {
                example.UpdatedExampleStatus += (x) => AnExampleText = x;
            }
            else
            {
                Debug.LogWarningFormat("No {0} Serialized in inspector.", typeof(ExampleModel).Name);
            }
        }
    }

    public class ExampleActiveModel : MonoBehaviour
    {
        public event Action<bool> UpdatedExampleStatus;

        public bool ModelActive
        {
            get
            {
                return gameObject.activeInHierarchy;
            }
        }

        public void OnEnable()
        {
            if (UpdatedExampleStatus != null)
            {
                UpdatedExampleStatus.Invoke(ModelActive);
            }
        }

        public void OnDisable()
        {
            if (UpdatedExampleStatus != null)
            {
                UpdatedExampleStatus.Invoke(ModelActive);
            }
        }
    }
   
    public class ActiveExampleView : MonoBehaviour
    {
        [SerializeField] private ExampleActiveModel example;
        [SerializeField] private Text exampleTextView;

        public bool AnExampleText
        {
            get
            {
                return exampleTextView.text == "Active";
            }
            set
            {
                exampleTextView.text = value ? "Active" : "Inactive";
            }
        }

        private void Awake()
        {
            if (example != null)
            {
                example.UpdatedExampleStatus += (x) => AnExampleText = x;
            }
            else
            {
                Debug.LogWarningFormat("No {0} Serialized in inspector.", typeof(ExampleModel).Name);
            }
        }
    }

The only gotcha I tend to find with the MVC pattern in Unity, is that literally all the visual elements ARE the view, and in terms of the UI buttons and OnTextEnd events ect, they also tend to be the controller. Depending on the depth of the functionality. Either way good luck dude.