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
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
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
- Properties have the
- Properties have 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.
[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.
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.