I have a Unity Zenject setup with a ProjectInstaller
with some global dependencies that adhere to a "modal" interface, e.g.,
public class ProjectInstaller : MonoInstaller {
public override void InstallBindings() {
Container.Bind<ModalManager>().AsSingle();
Container.Bind<Modal>().To<DialogManager>().AsSingle();
}
}
Some modals are only relevant to certain scenes, so I bind those in the SceneInstaller
:
public class SceneInstaller : MonoInstaller {
public override void InstallBindings() {
Container.BindInterfacesAndSelfTo<InventoryManager>()
.FromComponentInNewPrefab(InventoryPrefab)
.AsSingle()
}
}
I want to manage all modals from the single ModalManager
, defined at the project scope. So it has a List<Modal>
binding:
public class ModalManager : MonoBehaviour {
[Inject]
protected List<Modal> _modals;
}
When I run this, the ModalManager
only gets a single modal: the one defined in the project scope. In my understanding the SceneContext
is a subcontainer of the ProjectContext
. So I should be able to use FromSubContainerResolve
in the ProjectInstaller
to bind items in the child scene, perhaps by adding a line like:
// ProjectInstaller.cs
public override void InstallBindings() {
// ...
Container.Bind<Modal>().To<InventoryManager>().FromSubContainerResolve();
}
But I'm not sure which of the eleventy FromSubContainerResolve
methods make sense for this case. They all seem pertinent to prefabs with a game object context, not for use from within the ProjectContext
.
Does this use case make sense? Is there an easier or better way?
CodePudding user response:
The problem is that that ModalManager
can only be injected with dependencies that are added directly to ProjectContext. For these kinds of problems I recommend using the following pattern:
public interface IModal
{
}
public class ModalManager
{
private readonly List<IModal> _modals = new List<IModal>();
public IReadOnlyList<IModal> Modals
{
get { return _modals; }
}
public void AddModal(IModal modal)
{
_modals.Add(modal);
}
public bool RemoveModal(IModal modal)
{
return _modals.Remove(modal);
}
}
public class ModalRegisterHandler : IInitializable, IDisposable
{
private readonly List<IModal> _modals;
private readonly ModalManager _modalManager;
public ModalRegisterHandler(
// We need to use InjectSources.Local here, otherwise we will
// add any project context modals again in each scene
[Inject(Source = InjectSources.Local)]
List<IModal> modals, ModalManager modalManager)
{
_modals = modals;
_modalManager = modalManager;
}
public void Initialize()
{
foreach (var modal in _modals)
{
_modalManager.AddModal(modal);
}
}
public void Dispose()
{
// We don't want ModalManager to retain references to Modals defined in unloaded scenes
// (dispose is executed on scene unload)
foreach (var modal in _modals)
{
_modalManager.RemoveModal(modal);
}
}
}
public class SceneInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<IModal>().To<FooModal>();
Container.Bind<IModal>().To<BarModal>();
}
}
public class ProjectInstaller : MonoInstaller
{
public override void InstallBindings()
{
// We use CopyIntoDirectSubContainers so that ModalRegisterHandler gets automatically added to every
// scene context
Container.BindInterfacesTo<ModalRegisterHandler>().AsSingle().CopyIntoDirectSubContainers();
Container.Bind<ModalManager>().AsSingle();
Container.Bind<IModal>().To<QuxModal>();
Container.Bind<IModal>().To<FizzModal>();
}
}