Introduction
Many people think of programming data entry forms as very boring activity. I think so too. But sometimes I feel that something must be done to make this job easier. So I’ve developed this control to simplify designing and programming data entry forms.
Data entry forms are associated with documents, and enable the user to perform operations on associated documents, usually, operations such as creating, viewing, editing, and searching of documents. At any given time, a data entry form can be in one of several pre-defined states, depending on the operation that the user is currently performing. While designing a data entry form, the developer defines states which the form will maintain, and programs the logic that sets the form to a particular state in response to the user’s interaction. Say the user selects a menu option or clicks a button to enter a new document. When a form is set to a new state, some of its controls are enabled (become active) to process the user’s input while other controls are disabled. In some cases, programming such logic is not as simple as it seems. We need to take into account such factors as the document status, the user’s privileges and so on to give the user the access to only certain fields on a form. The State Controller provides design-time support for specifying the states for each control on a form in which this control is enabled, and/or states in which the control is visible. The State Controller also provides a method that sets a host form to the specified state at runtime.
Using the code
In order to use this control, while in design mode, you need to install it on the VS toolbox. To do that, select a tab you want to add this control to, right-click somewhere in your toolbox, and select "Add/Remove Items...", then navigate to the location of the StateController.DLL, select the DLL, and click OK. After that the control should be in your toolbox.
Properties
The control implements the IExtenderProvider
interface which means that it extends properties of other controls on a form. The control provides two extended properties: StateActive
and StateVisible
. These properties will appear in the property drid for the extendee control with the following naming format:
StateActve on StateController1
The control also provides its own properties:
- The
Items
property contains a reference to the list of states defined for the host form. A collection editor is associated with this property. This editor allows you to create and edit individual members of a collection. When adding a new state, you need to specify a state name, and optionally, a control on a form that receives focus when the form is set to this state.
- The
CurrentState
returns the name of the current state of the control.
Method
- The
SetState
is an overloaded method that sets a host form to the specified state. This method takes the state name as a parameter. The overloaded version accepts the second parameter which specifies a control that receives the focus when a new state is activated. If there is no state in the control’s state collection with the specified name, an ArgumentException
is thrown.
Event
- The
StateChanged
is raised when the control is set to a new state.
I created a demo project to show how this control can be used. The demo includes a data entry form that maintains the Customer table from the Northwind database. This form supports basic operations on the table – adding, editing, viewing, and searching records. I also wanted to demonstrate how to use this control to permit only users with certain privileges to have access to some fields. In this case, a user with super-user privileges can see the customer balance and edit the value of the Credit Limit field. So I defined the following states for this form:
Idle
– in this state, the current record of the table is shown, and the form waits for the user to make the choice.
New
- in this state, a new record is being entered.
Edit
- the state for editing an existing record.
Search
- this state permits the user to search a record in the table.
Idle SuperUser
- show the current record when the user has super-user privileges.
Edit SuperUser
– - edit record by the user with the super-user privileges.
The form is set to a new state in response to user interaction. The SetState
method is invoked within even handlers to set the form to an appropriate state. I think the source code gives you some ideas on how to use this control. I tried to make the demo as simple as possible.
Points of interest
The control is a subclass of the Component
class, and implements the IExtenderProvider
and ISupportInitialize
interfaces. I chose the implementation of the ISupportInitialize
interface just not to check properties when the control is loading in the Windows Forms designer.
[ToolboxBitmap(typeof(StateController))]
[ProvideProperty("StateActive", typeof(Control)),
ProvideProperty("StateVisible", typeof(Control)),
Designer(typeof(StateControllerDesigner)),
DefaultEvent("StateChanged"),
ToolboxItemFilter("System.Windows.Forms")]
public class StateController : System.ComponentModel.Component,
IExtenderProvider, ISupportInitialize
Two hash tables are used to stash the values of the properties extended for the form’s controls. The reference to a control is used as the key for the hash table entry, and the value of the property is stored in the value part of the hash table entry.
The CanExtend
method specifies what types of extendee controls are supported by the StateController
. In this case, all controls derived from the control class, except forms, are supported.
public bool CanExtend(object extendee)
{
return ((extendee is Control) && !(extendee is Form));
}
When implementing IExtenderProvider
, we need to create a pair of public methods for each extended property. These methods must be named Get<PropertyName>
and Set<PropertyName>
. These methods act like get/set parts of a regular property.
The GetStateActive
and SetStateActive
methods support the StateActive
property, and respectively GetStateVisible
and SetStateVisible
methods support the StateVisible
property.
[Description("Retrieve Active States descriptors for a control"),
Category("StateManagment"),Localizable(true),
Editor(typeof(SelectedStatesEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public virtual string GetStateActive(Control ctl)
{
return (string) this.m_activeStates[ctl];
}
public virtual void SetStateActive(Control ctl,string value)
{
if (this.DesignMode)
{
if (value!=null)
{
this.CleanInput(ref value);
if (!CheckValue(value))
throw new ArgumentException("Invalid value " +
value);
}
}
if (value!=null)
this.m_activeStates[ctl] = value;
else
this.m_activeStates.Remove(ctl);
}
The pair of methods ShouldSerialize<PropertyName>
and Reset<PropertyName>
prevent the serialization of empty values of the extended properties in the form’s code.
private bool ShouldSerializeStateActive(Control ctrl)
{
if ((this.m_activeStates[ctrl] == null) || (
((string)this.m_activeStates[ctrl]).Length == 0))
return false;
return true;
}
private void ResetStateActive(Control ctrl)
{
this.SetStateActive(ctrl, "");
}
The SetState
method sets a host form to the state specified by the first parameter. It makes two loops. In the first loop, the controls of the host form are enabled or disabled depending on the value of the StateActive
property for a control. The second loop analyzes the values of the StateVisible
property, and hides or shows controls.
The assembly contains the control class itself and auxiliary classes – type converters, UI editors, a control designer which facilitates the design-time behavior of the control, and the serialization of the control’s properties in the form code. Actually, I spent 90% of my time on programming and debugging these editors and converters.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.