App user settings
Suppose you're making an extension-app that defines a Translate
Excel function that uses the Google Translate API to translate text from one language to another.
Google's Translate API requires an API key to work. If you're only using your extension in-house, you could embed the API key inside the source code of your app. However, if you're publishing your app and you don't know who is going to be using it, you wouldn't want to include your own API key in the app, since users might run up a sizable bill on your account.
Ideally, you'd want the end user to supply their own API key instead, so you don't have to worry how much people are using it. This poses a question: where does the user enter their API key?
You could have the api key be an argument of the Translate
function. But this would be extremely inconvenient for the user because they would need to enter the (long and un-rememberable) API key every time they want to use Translate function. Worse still, the api key would then be saved inside the workbook so sharing the workbook would also share the (secret) API key.
The proper way to solve this problem is for the extension to allow the user to configure it. After installing the extension app, the user would configure it by entering their Google API key, which would be stored on the user's machine rather than inside the workbook. Furthermore, the user would only need to enter their API key in one place, instead of every time they use the Translate
function.
For this reason, each extension can define its own configuration UI.
The general approach
Handling settings involves the following four tasks:
- Defining a settings class
- Instantiating the settings object and registering it into the IOC container so other classes in the app can reference it
- Defining a UI for editing the settings object
- Ensuring the settings are persisted between sessions
The App
base class (ApplicationModule
) provides default implementations for tasks 2, 3 and 4, so in most cases you just need to define a class that will represent your settings.
Defining a settings class
To scaffold a settings class, use the "Add->Settings class" command in the context menu:
This will scaffold a simple class that looks something like this:
1 2 3 4 5 6 7 8 |
|
Compiling the code as-is gives the following UI in the "Configure extensions" dialog:
Important things to note about the generated class are:
- It implements the
ISettings
interface - Properties have the
[Trackable]
attribute - Properties have the
[Editable]
attribute
The ISettings
interface is a marker interface without any members. It's used to mark a class as the settings class. On startup, the App
object will scan the assembly looking for a class that implements this interface. If it finds one, it will instantiate it, register it with the container, and set up persistence for it. Scanning the project and instantiating the settings class is done by the virtual CreateSettingsObject
method which can be overriden in case different behavior is needed.
The [Trackable]
attribute marks a property for persistance. Only properties marked with this attribute are persisted between sessions.
The Jot library is used for persistence.
Marking a property with the [Editable]
attribute will make it show up in the property editor. The property editor is used as the default control for editing the configuration object, though users are free to supply their own UI.
Customizing the UI
The default UI for editing the configuration object is a property editor control. This is fine for most purposes and various customizations can be done using attributes.
If you would like to use an entirely different custom control for editing the configuration, you can override the GetConfigUI
method in your App
class and provide it.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
The above code would result in the following UI:
The GetConfigUI method accepts the settings object and returns a ConfigUI
instance. The ConfigUI
instance contains a reference to the WPF control that will be displayed in the "Configure extensions" dialog and, optionally, an action that should be executed when the user clicks "OK" in the dialog.
Simple user interfaces can be defined through code, but since QueryStorm doesn't have a WPF/XAML designer, more complex UIs can be tricky to define. In case a more complex UI is needed, it's best to implement it in Visual Studio, and then either copy the code from there into QueryStorm, or define the UI as a separate dll in Visual Studio and reference it from the QueryStorm project.