-
Notifications
You must be signed in to change notification settings - Fork 3
UsingStringInputDialog
The StringInputDialog is a special dialog added into Solid Shine UI that can be used to prompt users for some text, such as for naming/renaming something or initializing otherwise-readonly values. The dialog is simply a small window with some descriptive text, a textbox, and OK and Cancel buttons.
I created this dialog as there were situations, both while I was developing/debugging software and as part of the UX for my programs, where I needed a dialog where it would require some text to continue. After a while of custom building such dialogs by hand, I decided to create a more generic one that I could reuse.
Setting up the StringInputDialog is fairly straightforward, similar to, say, setting up an OpenFileDialog.
When creating a StringInputDialog, there are three constructors that you can use. The first constructor takes no parameters, and you can use that and then set all the properties afterwards. The other two constructors can be used to pre-fill some properties at construction, rather than setting properties afterwards.
The following three examples will produce the same results:
// first constructor with no parameters
StringInputDialog sid1 = new StringInputDialog();
sid1.ColorScheme = ColorScheme.CreateDarkTheme();
sid1.Title = "Window Title";
sid1.Description = "Enter some text:";
sid1.Value = "default text";
// second constructor, with a ColorScheme parameter
StringInputDialog sid2 = new StringInputDialog(ColorScheme.CreateDarkTheme());
sid2.Title = "Window Title";
sid2.Description = "Enter some text:";
sid2.Value = "default text";
// third constructor, with many parameters
StringInputDialog sid3 = new StringInputDialog(ColorScheme.CreateDarkTheme(),
"Window Title", "Enter some text:", "default text");
Similar to all other controls and dialogs in Solid Shine UI, the StringInputDialog supports using the ColorScheme
property to quickly set the appearance of the dialog. If you already have a ColorScheme set up and stored elsewhere in your app, you can simply pass it into this dialog's ColorScheme property, or otherwise you can just create one specifically for the dialog. You're also able to simply not provide a ColorScheme and the dialog will use a default value (with gray colors), or you can use the various brush properties instead.
Creating a ColorScheme is easy via either using new ColorScheme(Color)
(such as new ColorScheme(Colors.CornflowerBlue)
) to create a color scheme based around any color you want to use, or using ColorScheme.CreateLightTheme()
or ColorScheme.CreateDarkTheme()
to quickly get a basic color scheme that is useable in any scenario.
All of these visual options are optional, as the StringInputDialog will use a default gray color scheme if none is provided.
If you used the third constructor as listed above, the constructor will prompt you for things like the window's title and the description text to display within the dialog. Otherwise, if you didn't, that can be set via properties afterwards.
- The
Title
property sets the title of the window - pretty straightforward. Just like any other WPF window and also the SSUI FlatWindow, the title is what's displayed at the top of the window (and is also shown in places like the taskbar, Task Manager, and the Alt-Tab display). - The
Description
property sets the text that will be displayed right above the text box in the dialog. You can use this to describe what type of text you're expecting the user to enter and/or the limitations the user should be aware of, such as"Enter the name to use:"
,"Set the hexadecimal value (only 0-9, A-F)"
, or""
. Whatever seems fitting for the situation, but aim to keep the descriptions short and brief. - The
Value
property gets or sets the text that's actually in the text box. By setting this property prior to showing the dialog, you can provide a default value to put into the text box.
Displaying the dialog is as simple as running ShowDialog()
like any other dialog or window you want to use as a dialog. Again like any other dialog, this means the StringInputDialog will retain focus and the owner window cannot be focused until this dialog is closed.
Once the dialog is closed and execution is moved back to your window's code, you can use the DialogResult
property to check the result of the dialog. DialogResult
will return true
if the user closed the dialog by clicking the OK button (or pressing Enter, by default); in all other situations - such as clicking the Cancel button, pressing the Escape key, or closing the dialog via the Close button in the top-right - will return false
.
You can get the value that was entered in by using the Value
property.
private void DisplayStringInput()
{
StringInputDialog sid = new StringInputDialog();
sid.ColorScheme = ColorScheme.CreateLightTheme();
sid.Title = "Enter text";
sid.Description = "Type in some text:";
sid.OwnerWindow = Window.GetWindow(this);
sid.ShowDialog();
if (sid.DialogResult)
{
// the user pressed the OK button
string result = sid.Value;
}
else
{
// the user cancelled out of the dialog
}
}
To make the dialog easier to use from just the keyboard alone, a few convenience features are offered in the dialog; if desired, though, these can be turned off.
- When showing the dialog, the text box immediately gets keyboard focus. If there's a value already put into the text box when the dialog is shown, then by default the entire value is selected (to allow the user to quickly delete it and enter in a new value). The auto-selection can be turned off by setting
SelectTextOnFocus
to false. - Pressing Enter while the text box has focus will act the same as if the user presses the OK button (accepting the input and closing the dialog). This can be turned off by setting
EnterKeyConfirms
to false. - Pressing Escape while the text box has focus will act the same as if the user presses the Cancel button (closing the dialog without accepting the input). This can be turned off by setting
EscapeKeyCancels
to false.
Starting with Solid Shine UI 1.9.5, you can provide a validation function to provide users some quick feedback if the string they're entering matches a format you're expecting or not. For example, you can check if the text they entered can be parsed as a date or other type of value. If the inputted string doesn't pass the validation function, the OK button is disabled and a custom failure message is displayed.
To enable validation, you'll want to pass in a function to the ValidationFunction
property; this can be an anonymous function or a reference to an actual named function elsewhere in your code. The function needs a string parameter to take in the inputted string, and needs to return a boolean, with true
meaning the inputted string passes and is valid/useable and false
meaning the string fails and cannot be accepted.
A custom failure message can be displayed with the ValidationFailureString
property, preferably with some explanation as to what was expected and/or how the inputted string failed. You can also display a success message with the ValidationSuccessString
property, although it's generally recommended to keep this as string.Empty
to avoid unnecessary clutter. These messages are displayed immediately beneath the text box of the dialog.
The validation function is called every time the text in the text box changes, such as the user deleting or typing in a character. To prevent visual lag, I recommend the validation function be something that can return quickly to prevent blocking the UI thread; if necessary, lean more towards quick and dirty validation here and then more thoroughly check the string after the dialog is closed.
private void SetDecimal()
{
decimal _baseValue = 0.5M;
ColorScheme _cs = ColorScheme.CreateLightTheme();
StringInputDialog sid = new StringInputDialog(_cs, "Set Decimal",
"Enter in the exact decimal value to use:", _baseValue.ToString());
sid.ValidationFunction = (s) => { return decimal.TryParse(s, out _); };
sid.ValidationFailureString = "Not a valid decimal value";
sid.Owner = Window.GetWindow(this);
sid.ShowDialog();
if (sid.DialogResult)
{
_baseValue = decimal.Parse(sid.Value);
}
}
Here are a few examples for where the StringInputDialog can be used, based upon how I already use it in my code.
The dialogs example present in the SsuiSample program displays some text in a panel. By pressing the "Change Text..." button, the user is able to change its text by entering text in the StringInputDialog. I set the default value of the dialog to be what's currently already displayed in the text panel, and put a description of "Set the sample text to use:".
// by default, the sample text reads "Sample Text", but this method allows users to change it
// in other words, the control is initialized with txtSampleText.Text = "Sample Text"
private void btnSetText_Click(object sender, RoutedEventArgs e)
{
StringInputDialog sid = new StringInputDialog(ColorScheme, "Set Text", "Set the sample text to use:", txtSampleText.Text);
sid.Owner = Window.GetWindow(this);
sid.ShowDialog();
if (sid.DialogResult)
{
txtSampleText.Text = sid.Value;
}
}
The PropertyList control uses StringInputDialog in a number of situations where users are able to input a value as a string, which can be parsed into whatever format is needed.
Here is the example from the GuidEditor in PropertyList, which is for displaying and setting Guid structs.
Note that this example doesn't take advantage of the data validation feature, which I discussed above.
private void mnuSetGuid_Click(object sender, RoutedEventArgs e)
{
StringInputDialog sid = new StringInputDialog();
sid.Title = "Enter Guid";
sid.Description = "Enter in a valid Guid for this property:";
sid.ColorScheme = _cs;
sid.Owner = Window.GetWindow(this);
sid.ShowDialog();
if (sid.DialogResult)
{
bool res = Guid.TryParse(sid.Value, out Guid g);
if (res)
{
_guid = g;
// UI-updating functions and such
}
}
}
In situations where you need to prompt the user only once for some starter info, such as the client key for being able to connect with an API, the StringInputDialog can be useful.
Here is an example that is similar to what I use in one of my programs:
public void StartService()
{
if (string.IsNullOrWhiteSpace(_apiKey))
{
StringInputDialog sid = new StringInputDialog(Settings.ColorScheme, "Enter API Key", "To start this service, please enter the API key provided on the website:");
sid.Owner = this;
sid.Width = 530;
sid.ShowDialog();
if (sid.DialogResult)
{
_apiKey = sid.Value.Trim();
SaveSettings();
}
else
{
return;
}
}
// actual service-connecting code
}
Design is always a balance between displaying enough info for the user to do what is needed, but avoiding displaying too much that it starts to overwhelm them. With that in mind, here's a couple suggestions/considerations for while using this dialog (or other similar usage cases in your other code):
- Ideally, the overall program will be designed well enough that the user knows what's being asked without needing to read the
Description
text. However, this text is what the user will immediately see above the text box and will be what they reference to determine what is being asked of them, in case they forget or don't have any other context. With that in mind, an apt description should be used, better than the default"Enter a value:"
that the dialog is initialized with. - That being said, avoid making the
Description
text too long, as too much text will overwhelm or confuse the user, and they may end up not even reading it and instead try to guess what is being asked on their own. A succinct description that describes the type of data that needs to be entered (i.e. integer, date, etc.) and what it will be immediately used for should be enough, and nothing beyond that. Examples include"Enter name for this playlist:"
,"Enter the start date:"
,"Enter in the API key to connect with {service}:"
. - If you're using data validation by passing in a
ValidationFunction
, make sure that the function returns quickly to avoid blocking the UI thread. If necessary, do some quick and dirty validation for the dialog, and then more thoroughly check the string after the dialog is closed. - The text in the
Description
as well as the context provided by the rest of your program's UI should let the user know what type of data is needed, in case only some strings are valid; avoid situations where the user is seeing theValidationFailureString
too much because they don't know what is being asked of them. - Don't make the
ValidationFailureString
too lengthy or too accusatory, but simply remind the user what type of data is being expected and that what is entered doesn't match that. If desired, you can have your validation function change theValidationFailureString
based upon what caused the string to fail validation. - You can display a validation pass/success message with the
ValidationSuccessString
, but it's recommended to not do this, as this string is generally just extra unneeded clutter in the UI.