How to: Manually Process User Input in Editors
- 10 minutes to read
If you require a mask that is not included in the standard set of Input Masks, you can do one of the following:
Create and register a custom
DevExpress.Data.Mask.MaskManager
descendant.Create an API-based mask that allows you to inspect and modify every edit attempted by a user.
This article describes the second technique.
Limitations
API-based masks can only be created for the following editors:
Implementation Techniques
You need to declare a method that processes a user action (edit). This method accepts information about this edit, and responds to it (accepts/rejects the change, or otherwise modifies the editor value). Implementation details depend on how you want to apply a custom mask to editors.
- Specify an Action for an individual editor.
- Use this techique when you need an API-based mask for one specific editor. If your users are able to invoke the “Mask Settings” dialog (see the EditMaskSettings method), masks implemented in this manner are not visible in this dialog.
- Register a mask globally.
- Create and register a separate class that encapsulates change processing logic. This technique adds your API-based mask to the set of masks available in the “Mask Settings” dialog. A registered mask can be reapplied to a large number of supported editors.
Unregistered Mask for an Individual Editor
This section demonstrates how to create a sample custom mask for a standalone TextEdit editor. The mask ensures the editor’s text always uses the title case.
Key Takeaways
You need to assign a custom Action to the editor’s EnableCustomMaskTextInput method. This Action is performed each time a user attempts to edit the editor value.
Your Action must be of the DevExpress.Data.Mask.CustomTextMaskInputArgs type. This class exposes public properties that allow you to check current and projected editor values, and identify what edit a user attempts to perform.
Call the
SetResult
method to manually set the final editor value.Masks created in this manner are not available in the “Mask Settings” dialog.
Implementation Details
Whenever a user makes a change (types or erases a character, inserts a string, and so on), text editors invoke a callback passed to the EnableCustomMaskTextInput method. You can assign a custom Action to this method to override the default text processing logic. In other words, you can review and modify the editor’s new value.
An assigned Action must be of the DevExpress.Data.Mask.CustomTextMaskInputArgs type. Objects of this type expose the following properties that allow you to obtain the current editor state:
CurrentEditText — the editor’s previous value.
ResultEditText — the editor’s projected new value.
InsertedText — the text a user attempts to insert or remove.
CurrentCursorPosition — the current cursor placement.
ActionType — the type of user operation (regular input, “Backspace” key press, “Delete” key press, and others).
textEdit1.Properties.EnableCustomMaskTextInput(args => {
if (args.ActionType == CustomTextMaskInputAction.Delete) // Do something
});
Once you’ve checked the user operation type, and how this operation will affect the editor text, call one of the SetResult method overloads to assign a new editor value. For example, the following sample applies title case to the editor string.
textEdit1.Properties.EnableCustomMaskTextInput(args => {
if(args.IsCanceled)
return;
var textInfo = CultureInfo.InvariantCulture.TextInfo;
args.SetResult(textInfo.ToTitleCase(args.ResultEditText),
args.ResultCursorPosition, args.ResultSelectionAnchor);
});
Global Mask Registration
This section demonstrates a “Letter Case Style” custom mask implementation. The mask includes a parameter that allows users to choose between upper case, lower case, and title case. After registration, the “Letter Case Style” mask becomes available for all TextEdit and ButtonEdit editors in the registered namespace.
Key Takeaways
Create a descendant of the
DevExpress.Data.Mask.CustomTextMaskManager
class and override itsProcessCustomTextMaskInput
method. This method accepts objects of the same CustomTextMaskInputArgs type as Actions passed to individual editors (see the Unregistered Mask for an Individual Editor section). As a result, you should check the same properties and call the sameSetResult
method, as described in the previous section.Masks created in this manner are available in the “Mask Settings” dialog (call the EditMaskSettings() method to display this dialog at runtime). For example, the following mask allows users to select a letter case style:
- After registration, masks become available for supported editors, but remain inactive (editors do not automatically apply these masks). To apply a registered mask in code, create a custom
MaskSettings
descendant and call the editor’sConfigure
method.
Implementation Details
Create a Custom Mask Manager
Create a custom DevExpress.Data.Mask.CustomTextMaskManager
class descendant.
A class constructor method should have parameters that match mask settings available to users. For example, the sample “Letter Case Style” mask has a selector that allows users to choose between three case styles (title case, all uppercase, and all lowercase). This mode is passed to the constructor as a single @case
parameter.
public enum LetterCase {
// Use the System.ComponentModel.Description attribute to alter public names of enum values
Title,
Upper,
Lower
}
public class LetterCaseMaskManager : CustomTextMaskManager {
readonly LetterCase @case;
// Class constructor
public LetterCaseMaskManager(
LetterCase @case = LetterCase.Title) {
this.@case = @case;
}
}
Decorate your class constructor with the DevExpress.Data.Mask.MaskManager.Parameters
attribute to specify a namespace whose editors should be able to use this custom mask. Additionally, this attribute allows you to set a public mask name (the name users can see in the “Mask Settings” dialog).
namespace DXApplication1 {
public class LetterCaseMaskManager : CustomTextMaskManager {
// Parameter #1: Project namespace and custom mask type
// Parameter #2: Public mask name
[Parameters("DXApplication1, DXApplication1.LetterCaseMaskManager", "Letter Case Style")]
public LetterCaseMaskManager(
// Class constructor parameters
) {
// ...
}
}
}
Decorate constructor parameters with the DevExpress.Data.Mask.MaskManager.Parameter
attribute to set up the visibility and public name of mask options.
public class LetterCaseMaskManager : CustomTextMaskManager {
readonly LetterCase @case;
[Parameters("DXApplication1, DXApplication1.LetterCaseMaskManager", "Letter Case Style")]
public LetterCaseMaskManager(
[Parameter("case", "Case Style")]
// Parameter #1: Option's name
// Parameter #2: Option's public name
// Parameter #3: Visibility (not set in this sample)
LetterCase @case = LetterCase.Title) {
this.@case = @case;
}
}
Override Text Processing Logic
Override the ProcessCustomTextMaskInput
method. This method uses a parameter of the same CustomTextMaskInputArgs type as the EnableCustomMaskTextInput method. See the Unregistered Mask for an Individual Editor section for the description of the type’s public properties.
protected override void ProcessCustomTextMaskInput(CustomTextMaskInputArgs args) {
if (args.IsCanceled)
return;
var textInfo = CultureInfo.InvariantCulture.TextInfo;
switch (@case) {
case LetterCase.Title:
args.SetResult(textInfo.ToTitleCase(args.ResultEditText),
args.ResultCursorPosition, args.ResultSelectionAnchor);
break;
case LetterCase.Upper:
args.SetResult(textInfo.ToUpper(args.ResultEditText),
args.ResultCursorPosition, args.ResultSelectionAnchor);
break;
case LetterCase.Lower:
args.SetResult(textInfo.ToLower(args.ResultEditText),
args.ResultCursorPosition, args.ResultSelectionAnchor);
break;
}
}
Apply a Globally Registered Mask in Code
Create a custom MaskSettings descendant.
public abstract class LetterCaseMaskSettings : MaskSettings.User {
public class CaseStyle : MaskSettingsWithCulture {
protected override Type GetMaskManagerType() {
return typeof(LetterCaseMaskManager);
}
public LetterCase Case {
get { return GetValue<LetterCase>("case", LetterCase.Title); }
set { SetValue("case", value); }
}
}
}
Use custom settings to set an active mask with the Configure
method. See this article for more information: Input Masks.
var settings = textEdit1.Properties.MaskSettings.Configure<LetterCaseMaskSettings.CaseStyle>();
settings.Case = LetterCase.Title;
Complete Code
The complete code of the sample “Letter Case Style” mask registered for all supported editors is as follows:
using DevExpress.Data.Mask;
using DevExpress.XtraEditors.Mask;
using System;
using System.ComponentModel;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace DXApplication1 {
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
public Form1() {
InitializeComponent();
// Apply a custom mask
var settings = textEdit1.Properties.MaskSettings.Configure<LetterCaseMaskSettings.CaseStyle>();
settings.Case = LetterCase.Title;
}
}
public enum LetterCase {
[Description("Title case")]
Title,
[Description("All caps")]
Upper,
[Description("All lowercase")]
Lower
}
// Custom mask manager
public class LetterCaseMaskManager : CustomTextMaskManager {
readonly LetterCase @case;
[Parameters("DXApplication1, DXApplication1.LetterCaseMaskManager", "Letter Case Style")]
public LetterCaseMaskManager(
[Parameter("case", "Case Style")]
LetterCase @case = LetterCase.Upper) {
this.@case = @case;
}
// Editor text processing logic
protected override void ProcessCustomTextMaskInput(CustomTextMaskInputArgs args) {
if (args.IsCanceled)
return;
var textInfo = CultureInfo.InvariantCulture.TextInfo;
switch (@case) {
case LetterCase.Title:
args.SetResult(textInfo.ToTitleCase(args.ResultEditText),
args.ResultCursorPosition, args.ResultSelectionAnchor);
break;
case LetterCase.Upper:
args.SetResult(textInfo.ToUpper(args.ResultEditText),
args.ResultCursorPosition, args.ResultSelectionAnchor);
break;
case LetterCase.Lower:
args.SetResult(textInfo.ToLower(args.ResultEditText),
args.ResultCursorPosition, args.ResultSelectionAnchor);
break;
}
}
}
// Custom mask settings (required for the Configure method)
public abstract class LetterCaseMaskSettings : MaskSettings.User {
public class CaseStyle : MaskSettingsWithCulture {
protected override Type GetMaskManagerType() {
return typeof(LetterCaseMaskManager);
}
public LetterCase Case {
get { return GetValue<LetterCase>("case", LetterCase.Title); }
set { SetValue("case", value); }
}
}
}
}