Send data from modal screen (dialog) to another screen

Thank you for this example. It helped me a lot!

1 Like

These techniques are definitely useful and Iā€™ve learned some things from this discussion. Out of curiosity, I would have just put the shared variable in a module that was available to both screens. What are the downsides to doing that?

Some folk consider this the Xojo equivalent of ā€˜global variablesā€™ and frown upon them.
But it does work perfectly well, and makes a lot of sense.
Imagine your app needs to know what the userā€™s email address is, and displays it on 3 different screens.
You might store it (encrypted?) as a preference setting, but load it into such a ā€˜globalā€™ variable when the app starts, and refer to it whenever it is needed.

One downside is that - as a basic variable type - if you amend the value, it would not propagate to open windows automatically.
They would need to poll the value now and then to keep up to date.
Thatā€™s not great UI

I can imagine a few ways around that.
One would be to make the variable a computed property, so that when the value is ā€˜setā€™, it has the ability to push new values to any open window.

The discussion above has talked about giving a window a property of ā€˜parentā€™, through which it can communicate to another screen.
In iOS apps, I suspect there is usually only ever 1 settings screen, 1 design screen, 1 password screen at any time, (because you donā€™t have the multi window expectation of desktop)

So my own solution to all of this is a combination of the ā€˜module-level variableā€™ and the ā€˜referring to a variable which is another screenā€™

In the module I have Public variables like this

theSettingsScreen as wndSettings
thePasswordScreen as wndPassword
theDesignScreen as wndDesigner

When these actual screens open, the first line in Opening is


theDesignScreen = self

And then I can ā€˜talkā€™ to this screen from any other screen like this:

if theDesignScreen <> nil then theDesignScreen.methodName
if theDesignScreen <> nil then theDesignScreen.property3 = EmailAddress

This particular method only really works as long as there can only be a single instance of any window type.
(Its fine to close an instance and create a new one as long as there is only one of any type at any time)

These setting changes could / should be wrapped up in special methods for the purpose, as Greg suggests, or these special methods could be the ā€˜setā€™ part of computed variables.

1 Like

Another way to handle this is to use a messaging system where you literally send the updated value to any class thatā€™s listening for changes.

Typically I would create a module called Messaging and inside that, a Class Interface named Receiver with a single method:

Sub MessageReceived(name as String, data as Variant)

To the module you add a property and two methods:

Private mReceivers() as WeakRef

Sub AddReceiver(obj as Messaging.Receiver)
  mReceivers.add new WeakRef(obj)
End Sub
Sub Send(name as String, data as variant = nil)
  For I as integer = 0 to mReceivers.lastIndex
    If mReceivers(i) <> nil and mReceivers(i).value <> nil then
      Receiver(mReceivers(i).value).MessageReceived(name, data)
    Else
      mReceivers.RemoveAt(i)
      i = i - 1
    End if
  Next I
End Sub

Once this has been added to your project, youā€™ll need to add a line of code to the Opening event of any class that needs to receive data.

Messaging.AddReceiver(Self)

ā€¦And add the Class Interface to the class, either by right-clicking the item in the navigator and selecting Add Interface or by clicking the Interfaces button below the Super field in the Inspector. In that dialog, check the box for Messaging.Receiver. Make sure the checkbox for creating the method(s) is checked at the bottom, something about #pragma error IIRC. then click OK. Your class should now have a method called MessageReceived with a signature that we defined above.

Nowā€¦ whenever you need to send a value to one or more receivers, you just write code like this:

Messaging.Send(ā€œuser ageā€, 23)

All of the classes that registered to receive messages (that still exist) will be notified of the change.

A quick note about the WeakRefs: The reason why theyā€™re used here is so that the messaging system doesnā€™t inadvertently hold references to all of the classes that are listeners. Otherwise your app would leak receivers if you didnā€™t remember to remove them. I just find this way easier in the long run.

3 Likes

I use this pattern in all my apps and it is extremely useful.

It is used to

  • update values entered by the user
  • telling the parent screen that something changed in a presented modal screen
  • notify all open screens and singleton classes that the user made an in-app purchase and update the UI / unlock some features
  • Communicate between App.HandleURL and the presented screen
  • ā€¦

I also extended the pattern a bit to send a delayed message when the receiver hasnā€™t been instantiated yet:

Messaging.SendDelayed(afterMsec as Integer, name as string, data as Variant)

4 Likes